diff --git a/account_check_deposit/README.rst b/account_check_deposit/README.rst new file mode 100644 index 000000000..12fb31dca --- /dev/null +++ b/account_check_deposit/README.rst @@ -0,0 +1,69 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============= +Check Deposit +============= + +This module allows you to easily manage check deposits : you can select all +the checks you received and create a global deposit for the +selected checks. This module supports multi-currency ; each deposit has a currency +and all the checks of the deposit must have the same currency +(so, if you have checks in EUR and checks in USD, you must create 2 deposits : +one in EUR and one in USD). + +Configuration +============= + +A journal named *Check Received* is automatically created. It will be available as a payment method in Odoo. On this journal, you must configure the *Default Debit Account* and *Defaut Credit Account* ; this is the account via which the amounts of checks will transit between the reception of a check from a customer and the validation of the check deposit in Odoo. + +On the company form view, you should configure the *Account for Check Deposits* ; this is the account via which the amounts of checks will transit between the validation of the check deposit in Odoo and the credit on the bank account. + +Usage +===== + +When you receive a check that pays a customer invoice, you can go to that invoice and click on the button *Register Payment* and select the *Check Received* journal as *Payment Method*. + +When you want to deposit checks to the bank, go to the menu *Accounting > Bank and Cash > Check Deposit*, create a new check deposit and set the journal *Checks Received* and select the bank account on which you want to credit the checks. Then click on *Add an item* to select the checks you want to deposit at the bank. Eventually, validate the deposit and print the report (you probably want to customize this report). + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/92/8.0 + +For further information, please visit: + + * https://www.odoo.com/forum/help-1 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed feedback +`here `_. + +Credits +======= + +Contributors +------------ + +* Benoît GUILLOT +* Chafique DELLI +* Alexis de Lattre + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/account_check_deposit/__init__.py b/account_check_deposit/__init__.py index f1c1297b1..a98c879ff 100644 --- a/account_check_deposit/__init__.py +++ b/account_check_deposit/__init__.py @@ -1,22 +1,3 @@ # -*- coding: utf-8 -*- -############################################################################### -# -# account_check_deposit for Odoo/OpenERP -# Copyright (C) 2012-2015 Akretion (http://www.akretion.com/) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################### from . import account_deposit diff --git a/account_check_deposit/__manifest__.py b/account_check_deposit/__manifest__.py index 1536a39ed..f387103ec 100644 --- a/account_check_deposit/__manifest__.py +++ b/account_check_deposit/__manifest__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################### # -# account_check_deposit for Odoo/OpenERP +# account_check_deposit for Odoo # Copyright (C) 2012-2015 Akretion (http://www.akretion.com/) # @author: Benoît GUILLOT # @author: Chafique DELLI @@ -28,18 +28,6 @@ 'category': 'Accounting & Finance', 'license': 'AGPL-3', 'summary': 'Manage deposit of checks to the bank', - 'description': """ -Account Check Deposit -===================== -This module allows you to easily manage check deposits : you can select all -the checks you received as payments and create a global deposit for the -selected checks. - -A journal for received checks is automatically created. -You must configure on this journal the default debit account and the default -credit account. You must also configure on the company the account for -check deposits. -""", 'author': "Akretion,Odoo Community Association (OCA)", 'website': 'http://www.akretion.com/', 'depends': [ diff --git a/account_check_deposit/account_data.xml b/account_check_deposit/account_data.xml index 141123f54..a2c525ec6 100644 --- a/account_check_deposit/account_data.xml +++ b/account_check_deposit/account_data.xml @@ -1,6 +1,5 @@ Check Received CHK bank - diff --git a/account_check_deposit/account_deposit.py b/account_check_deposit/account_deposit.py index cdc0fbd2c..8f40887b0 100644 --- a/account_check_deposit/account_deposit.py +++ b/account_check_deposit/account_deposit.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################### # -# account_check_deposit for Odoo/OpenERP +# account_check_deposit for Odoo # Copyright (C) 2012-2015 Akretion (http://www.akretion.com/) # @author: Benoît GUILLOT # @author: Chafique DELLI @@ -22,19 +22,24 @@ # ############################################################################### -from openerp.osv import fields, orm -from openerp.tools.translate import _ +from openerp import models, fields, api, _ import openerp.addons.decimal_precision as dp +from openerp.exceptions import ValidationError +from openerp.exceptions import Warning as UserError -class account_check_deposit(orm.Model): +class AccountCheckDeposit(models.Model): _name = "account.check.deposit" _description = "Account Check Deposit" _order = 'deposit_date desc' - def _compute_check_deposit(self, cr, uid, ids, name, args, context=None): - res = {} - for deposit in self.browse(cr, uid, ids, context=context): + @api.multi + @api.depends( + 'company_id', 'currency_id', 'check_payment_ids.debit', + 'check_payment_ids.amount_currency', + 'move_id.line_id.reconcile_id') + def _compute_check_deposit(self): + for deposit in self: total = 0.0 count = 0 reconcile = False @@ -51,82 +56,70 @@ class account_check_deposit(orm.Model): for line in deposit.move_id.line_id: if line.debit > 0 and line.reconcile_id: reconcile = True - res[deposit.id] = { - 'total_amount': total, - 'is_reconcile': reconcile, - 'currency_none_same_company_id': currency_none_same_company_id, - 'check_count': count, - } - return res + deposit.total_amount = total + deposit.is_reconcile = reconcile + deposit.currency_none_same_company_id =\ + currency_none_same_company_id + deposit.check_count = count - _columns = { - 'name': fields.char( - 'Name', size=64, readonly=True), - 'check_payment_ids': fields.one2many( - 'account.move.line', 'check_deposit_id', 'Check Payments', - states={'done': [('readonly', '=', True)]}), - 'deposit_date': fields.date( - 'Deposit Date', required=True, - states={'done': [('readonly', '=', True)]}), - 'journal_id': fields.many2one( - 'account.journal', 'Journal', domain=[('type', '=', 'bank')], - required=True, states={'done': [('readonly', '=', True)]}), - 'journal_default_account_id': fields.related( - 'journal_id', 'default_debit_account_id', type='many2one', - relation='account.account', - string='Default Debit Account of the Journal'), - 'currency_id': fields.many2one( - 'res.currency', 'Currency', required=True, - states={'done': [('readonly', '=', True)]}), - 'currency_none_same_company_id': fields.function( - _compute_check_deposit, type='many2one', - relation='res.currency', multi='deposit', - string='Currency (False if same as company)'), - 'state': fields.selection([ - ('draft', 'Draft'), - ('done', 'Done'), - ], 'Status', readonly=True), - 'move_id': fields.many2one( - 'account.move', 'Journal Entry', readonly=True), - 'partner_bank_id': fields.many2one( - 'res.partner.bank', 'Bank Account', required=True, - domain="[('company_id', '=', company_id)]", - states={'done': [('readonly', '=', True)]}), - 'line_ids': fields.related( - 'move_id', 'line_id', relation='account.move.line', - type='one2many', string='Lines', readonly=True), - 'company_id': fields.many2one( - 'res.company', 'Company', required=True, - change_default=True, - states={'done': [('readonly', '=', True)]}), - 'total_amount': fields.function( - _compute_check_deposit, multi='deposit', - string="Total Amount", readonly=True, - type="float", digits_compute=dp.get_precision('Account')), - 'check_count': fields.function( - _compute_check_deposit, multi='deposit', readonly=True, - string="Number of Checks", type="integer"), - 'is_reconcile': fields.function( - _compute_check_deposit, multi='deposit', readonly=True, - string="Reconcile", type="boolean"), - } + name = fields.Char(string='Name', size=64, readonly=True, default='/') + check_payment_ids = fields.One2many( + 'account.move.line', 'check_deposit_id', string='Check Payments', + states={'done': [('readonly', '=', True)]}) + deposit_date = fields.Date( + string='Deposit Date', required=True, + states={'done': [('readonly', '=', True)]}, + default=fields.Date.context_today) + journal_id = fields.Many2one( + 'account.journal', string='Journal', domain=[('type', '=', 'bank')], + required=True, states={'done': [('readonly', '=', True)]}) + journal_default_account_id = fields.Many2one( + 'account.account', related='journal_id.default_debit_account_id', + string='Default Debit Account of the Journal') + currency_id = fields.Many2one( + 'res.currency', string='Currency', required=True, + states={'done': [('readonly', '=', True)]}) + currency_none_same_company_id = fields.Many2one( + 'res.currency', compute='_compute_check_deposit', + string='Currency (False if same as company)') + state = fields.Selection([ + ('draft', 'Draft'), + ('done', 'Done'), + ], string='Status', default='draft', readonly=True) + move_id = fields.Many2one( + 'account.move', string='Journal Entry', readonly=True) + partner_bank_id = fields.Many2one( + 'res.partner.bank', string='Bank Account', required=True, + domain="[('company_id', '=', company_id)]", + states={'done': [('readonly', '=', True)]}) + line_ids = fields.One2many( + 'account.move.line', related='move_id.line_id', + string='Lines', readonly=True) + company_id = fields.Many2one( + 'res.company', string='Company', required=True, + states={'done': [('readonly', '=', True)]}, + default=lambda self: self.env['res.company']._company_default_get( + 'account.check.deposit')) + total_amount = fields.Float( + compute='_compute_check_deposit', + string="Total Amount", readonly=True, + digits=dp.get_precision('Account')) + check_count = fields.Integer( + compute='_compute_check_deposit', readonly=True, + string="Number of Checks") + is_reconcile = fields.Boolean( + compute='_compute_check_deposit', readonly=True, + string="Reconcile") - _defaults = { - 'name': '/', - 'deposit_date': fields.date.context_today, - 'state': 'draft', - 'company_id': lambda self, cr, uid, c: self.pool['res.company']. - _company_default_get(cr, uid, 'account.check.deposit', context=c), - } - - def _check_deposit(self, cr, uid, ids): - for deposit in self.browse(cr, uid, ids): + @api.multi + @api.constrains('currency_id', 'check_payment_ids', 'company_id') + def _check_deposit(self): + for deposit in self: deposit_currency = deposit.currency_id if deposit_currency == deposit.company_id.currency_id: for line in deposit.check_payment_ids: if line.currency_id: - raise orm.except_orm( - _('Error:'), + raise ValidationError( _("The check with amount %s and reference '%s' " "is in currency %s but the deposit is in " "currency %s.") % ( @@ -136,35 +129,27 @@ class account_check_deposit(orm.Model): else: for line in deposit.check_payment_ids: if line.currency_id != deposit_currency: - raise orm.except_orm( - _('Error:'), + raise ValidationError( _("The check with amount %s and reference '%s' " "is in currency %s but the deposit is in " "currency %s.") % ( line.debit, line.ref or '', line.currency_id.name, deposit_currency.name)) - return True - _constraints = [( - _check_deposit, - "All the checks of the deposit must be in the currency of the deposit", - ['currency_id', 'check_payment_ids', 'company_id'] - )] - - def unlink(self, cr, uid, ids, context=None): - for deposit in self.browse(cr, uid, ids, context=context): + @api.multi + def unlink(self): + for deposit in self: if deposit.state == 'done': - raise orm.except_orm( - _('Error!'), + raise UserError( _("The deposit '%s' is in valid state, so you must " "cancel it before deleting it.") % deposit.name) - return super(account_check_deposit, self).unlink( - cr, uid, ids, context=context) + return super(AccountCheckDeposit, self).unlink() - def backtodraft(self, cr, uid, ids, context=None): - for deposit in self.browse(cr, uid, ids, context=context): + @api.multi + def backtodraft(self): + for deposit in self: if deposit.move_id: # It will raise here if journal_id.update_posted = False deposit.move_id.button_cancel() @@ -175,29 +160,30 @@ class account_check_deposit(orm.Model): deposit.write({'state': 'draft'}) return True - def create(self, cr, uid, vals, context=None): + @api.model + def create(self, vals): if vals.get('name', '/') == '/': - vals['name'] = self.pool['ir.sequence'].\ - next_by_code(cr, uid, 'account.check.deposit') - return super(account_check_deposit, self).\ - create(cr, uid, vals, context=context) + vals['name'] = self.env['ir.sequence'].\ + next_by_code('account.check.deposit') + return super(AccountCheckDeposit, self).create(vals) - def _prepare_account_move_vals(self, cr, uid, deposit, context=None): + @api.model + def _prepare_account_move_vals(self, deposit): date = deposit.deposit_date - period_obj = self.pool['account.period'] - period_ids = period_obj.find(cr, uid, dt=date, context=context) + period_obj = self.env['account.period'] + period_ids = period_obj.find(dt=date) # period_ids will always have a value, cf the code of find() move_vals = { 'journal_id': deposit.journal_id.id, 'date': date, - 'period_id': period_ids[0], + 'period_id': period_ids[0].id, 'name': _('Check Deposit %s') % deposit.name, 'ref': deposit.name, } return move_vals - def _prepare_move_line_vals( - self, cr, uid, line, context=None): + @api.model + def _prepare_move_line_vals(self, line): assert (line.debit > 0), 'Debit must have a value' return { 'name': _('Check Deposit - Ref. Check %s') % line.ref, @@ -209,9 +195,9 @@ class account_check_deposit(orm.Model): 'amount_currency': line.amount_currency * -1, } + @api.model def _prepare_counterpart_move_lines_vals( - self, cr, uid, deposit, total_debit, total_amount_currency, - context=None): + self, deposit, total_debit, total_amount_currency): return { 'name': _('Check Deposit %s') % deposit.name, 'debit': total_debit, @@ -222,119 +208,74 @@ class account_check_deposit(orm.Model): 'amount_currency': total_amount_currency, } - def validate_deposit(self, cr, uid, ids, context=None): - am_obj = self.pool['account.move'] - aml_obj = self.pool['account.move.line'] - if context is None: - context = {} - for deposit in self.browse(cr, uid, ids, context=context): - move_vals = self._prepare_account_move_vals( - cr, uid, deposit, context=context) - context['journal_id'] = move_vals['journal_id'] - context['period_id'] = move_vals['period_id'] - move_id = am_obj.create(cr, uid, move_vals, context=context) + @api.multi + def validate_deposit(self): + am_obj = self.env['account.move'] + aml_obj = self.env['account.move.line'] + for deposit in self: + move_vals = self._prepare_account_move_vals(deposit) + move = am_obj.create(move_vals) total_debit = 0.0 total_amount_currency = 0.0 - to_reconcile_line_ids = [] + to_reconcile_lines = [] for line in deposit.check_payment_ids: total_debit += line.debit total_amount_currency += line.amount_currency - line_vals = self._prepare_move_line_vals( - cr, uid, line, context=context) - line_vals['move_id'] = move_id - move_line_id = aml_obj.create( - cr, uid, line_vals, context=context) - to_reconcile_line_ids.append([line.id, move_line_id]) + line_vals = self._prepare_move_line_vals(line) + line_vals['move_id'] = move.id + move_line = aml_obj.create(line_vals) + to_reconcile_lines.append(line + move_line) # Create counter-part if not deposit.company_id.check_deposit_account_id: - raise orm.except_orm( - _('Configuration Error:'), + raise UserError( _("Missing Account for Check Deposits on the " "company '%s'.") % deposit.company_id.name) counter_vals = self._prepare_counterpart_move_lines_vals( - cr, uid, deposit, total_debit, total_amount_currency, - context=context) - counter_vals['move_id'] = move_id - aml_obj.create(cr, uid, counter_vals, context=context) + deposit, total_debit, total_amount_currency) + counter_vals['move_id'] = move.id + aml_obj.create(counter_vals) - am_obj.post(cr, uid, [move_id], context=context) - deposit.write({'state': 'done', 'move_id': move_id}) + move.post() + deposit.write({'state': 'done', 'move_id': move.id}) # We have to reconcile after post() - for reconcile_line_ids in to_reconcile_line_ids: - aml_obj.reconcile( - cr, uid, reconcile_line_ids, context=context) + for reconcile_lines in to_reconcile_lines: + reconcile_lines.reconcile() return True - def onchange_company_id( - self, cr, uid, ids, company_id, currency_id, context=None): - vals = {} - if company_id: - company = self.pool['res.company'].browse( - cr, uid, company_id, context=context) - if currency_id: - if company.currency_id.id == currency_id: - vals['currency_none_same_company_id'] = False - else: - vals['currency_none_same_company_id'] = currency_id - partner_bank_ids = self.pool['res.partner.bank'].search( - cr, uid, [('company_id', '=', company_id)], context=context) - if len(partner_bank_ids) == 1: - vals['partner_bank_id'] = partner_bank_ids[0] + @api.onchange('company_id') + def onchange_company_id(self): + if self.company_id: + partner_banks = self.env['res.partner.bank'].search( + [('company_id', '=', self.company_id.id)]) + if len(partner_banks) == 1: + self.partner_bank_id = partner_banks[0] else: - vals['currency_none_same_company_id'] = False - vals['partner_bank_id'] = False - return {'value': vals} + self.partner_bank_id = False - def onchange_journal_id(self, cr, uid, ids, journal_id, context=None): - vals = {} - if journal_id: - journal = self.pool['account.journal'].browse( - cr, uid, journal_id, context=context) - vals['journal_default_account_id'] = \ - journal.default_debit_account_id.id - if journal.currency: - vals['currency_id'] = journal.currency.id + @api.onchange('journal_id') + def onchange_journal_id(self): + if self.journal_id: + if self.journal_id.currency: + self.currency_id = self.journal_id.currency else: - vals['currency_id'] = journal.company_id.currency_id.id - else: - vals['journal_default_account_id'] = False - return {'value': vals} - - def onchange_currency_id( - self, cr, uid, ids, currency_id, company_id, context=None): - vals = {} - if currency_id and company_id: - company = self.pool['res.company'].browse( - cr, uid, company_id, context=context) - if company.currency_id.id == currency_id: - vals['currency_none_same_company_id'] = False - else: - vals['currency_none_same_company_id'] = currency_id - else: - vals['currency_none_same_company_id'] = False - return {'value': vals} + self.currency_id = self.journal_id.company_id.currency_id -class account_move_line(orm.Model): +class AccountMoveLine(models.Model): _inherit = "account.move.line" - _columns = { - 'check_deposit_id': fields.many2one( - 'account.check.deposit', - 'Check Deposit'), - } + check_deposit_id = fields.Many2one( + 'account.check.deposit', string='Check Deposit', copy=False) -class res_company(orm.Model): +class ResCompany(models.Model): _inherit = 'res.company' - _columns = { - 'check_deposit_account_id': fields.many2one( - 'account.account', 'Account for Check Deposits', - domain=[ - ('type', '<>', 'view'), - ('type', '<>', 'closed'), - ('reconcile', '=', True)]), - } + check_deposit_account_id = fields.Many2one( + 'account.account', string='Account for Check Deposits', copy=False, + domain=[ + ('type', '<>', 'view'), + ('type', '<>', 'closed'), + ('reconcile', '=', True)]) diff --git a/account_check_deposit/account_deposit_sequence.xml b/account_check_deposit/account_deposit_sequence.xml index 47a53f88e..07c4c13bd 100644 --- a/account_check_deposit/account_deposit_sequence.xml +++ b/account_check_deposit/account_deposit_sequence.xml @@ -1,6 +1,6 @@