From 44409e5d787565555b5d2c273bc575f82efac0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20ALTUNBAY?= Date: Fri, 31 Jan 2020 12:57:19 +0300 Subject: [PATCH] check module added --- account_check/ANALISIS CHEQUES.ods | Bin 0 -> 21825 bytes account_check/__init__.py | 6 + account_check/__manifest__.py | 36 + .../data/account_payment_method_data.xml | 26 + account_check/models/__init__.py | 14 + .../models/account_bank_statement_line.py | 59 ++ .../models/account_chart_template.py | 85 ++ account_check/models/account_check.py | 831 ++++++++++++++++++ account_check/models/account_checkbook.py | 153 ++++ account_check/models/account_invoice.py | 49 ++ account_check/models/account_journal.py | 142 +++ account_check/models/account_payment.py | 627 +++++++++++++ account_check/models/res_company.py | 47 + account_check/models/res_config_settings.py | 19 + .../security/account_check_security.xml | 18 + account_check/security/ir.model.access.csv | 7 + .../views/account_chart_template_view.xml | 15 + account_check/views/account_check_view.xml | 266 ++++++ .../views/account_checkbook_view.xml | 44 + .../views/account_journal_dashboard_view.xml | 49 ++ account_check/views/account_journal_view.xml | 14 + account_check/views/account_payment_view.xml | 111 +++ .../views/res_config_settings_view.xml | 27 + account_check/views/res_partner_views.xml | 30 + account_check/wizard/__init__.py | 6 + .../wizard/account_check_action_wizard.py | 56 ++ .../account_check_action_wizard_view.xml | 34 + .../wizard/print_pre_numbered_checks.py | 20 + .../wizard/print_pre_numbered_checks_view.xml | 23 + account_payment_fix/README.rst | 69 ++ account_payment_fix/__init__.py | 1 + account_payment_fix/__manifest__.py | 16 + account_payment_fix/models/__init__.py | 3 + account_payment_fix/models/account_invoice.py | 24 + account_payment_fix/models/account_payment.py | 207 +++++ .../views/account_payment_view.xml | 51 ++ 36 files changed, 3185 insertions(+) create mode 100644 account_check/ANALISIS CHEQUES.ods create mode 100644 account_check/__init__.py create mode 100644 account_check/__manifest__.py create mode 100644 account_check/data/account_payment_method_data.xml create mode 100644 account_check/models/__init__.py create mode 100644 account_check/models/account_bank_statement_line.py create mode 100644 account_check/models/account_chart_template.py create mode 100644 account_check/models/account_check.py create mode 100644 account_check/models/account_checkbook.py create mode 100644 account_check/models/account_invoice.py create mode 100644 account_check/models/account_journal.py create mode 100644 account_check/models/account_payment.py create mode 100644 account_check/models/res_company.py create mode 100644 account_check/models/res_config_settings.py create mode 100644 account_check/security/account_check_security.xml create mode 100644 account_check/security/ir.model.access.csv create mode 100644 account_check/views/account_chart_template_view.xml create mode 100644 account_check/views/account_check_view.xml create mode 100644 account_check/views/account_checkbook_view.xml create mode 100644 account_check/views/account_journal_dashboard_view.xml create mode 100644 account_check/views/account_journal_view.xml create mode 100644 account_check/views/account_payment_view.xml create mode 100644 account_check/views/res_config_settings_view.xml create mode 100644 account_check/views/res_partner_views.xml create mode 100644 account_check/wizard/__init__.py create mode 100644 account_check/wizard/account_check_action_wizard.py create mode 100644 account_check/wizard/account_check_action_wizard_view.xml create mode 100644 account_check/wizard/print_pre_numbered_checks.py create mode 100644 account_check/wizard/print_pre_numbered_checks_view.xml create mode 100644 account_payment_fix/README.rst create mode 100644 account_payment_fix/__init__.py create mode 100644 account_payment_fix/__manifest__.py create mode 100644 account_payment_fix/models/__init__.py create mode 100644 account_payment_fix/models/account_invoice.py create mode 100644 account_payment_fix/models/account_payment.py create mode 100644 account_payment_fix/views/account_payment_view.xml diff --git a/account_check/ANALISIS CHEQUES.ods b/account_check/ANALISIS CHEQUES.ods new file mode 100644 index 0000000000000000000000000000000000000000..35f1ba3a8321cc645caac5f4fa08c1c664183de6 GIT binary patch literal 21825 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 + + + + + Received Third Check + received_third_check + inbound + + + + Delivered Third Check + delivered_third_check + outbound + + + + Issue Check + issue_check + outbound + + + + + + diff --git a/account_check/models/__init__.py b/account_check/models/__init__.py new file mode 100644 index 00000000..703c33ab --- /dev/null +++ b/account_check/models/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from . import account_journal +from . import account_invoice +from . import account_checkbook +from . import account_check +from . import account_payment +from . import res_company +from . import account_chart_template +from . import account_bank_statement_line +from . import res_config_settings diff --git a/account_check/models/account_bank_statement_line.py b/account_check/models/account_bank_statement_line.py new file mode 100644 index 00000000..b67f36cf --- /dev/null +++ b/account_check/models/account_bank_statement_line.py @@ -0,0 +1,59 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import models, api, _ +from odoo.exceptions import ValidationError +import logging +_logger = logging.getLogger(__name__) + + +class AccountBankStatementLine(models.Model): + _inherit = "account.bank.statement.line" + + @api.multi + def button_cancel_reconciliation(self): + """ Delete operation of checks that are debited from statement + """ + for st_line in self.filtered('move_name'): + if st_line.journal_entry_ids.filtered( + lambda x: + x.payment_id.payment_reference == st_line.move_name): + check_operation = self.env['account.check.operation'].search( + [('origin', '=', + 'account.bank.statement.line,%s' % st_line.id)]) + check_operation.check_id._del_operation(st_line) + return super( + AccountBankStatementLine, self).button_cancel_reconciliation() + + def process_reconciliation( + self, counterpart_aml_dicts=None, payment_aml_rec=None, + new_aml_dicts=None): + """ + Si el move line de contrapartida es un cheque entregado, entonces + registramos el debito desde el extracto en el cheque + TODO: por ahora si se cancela la linea de extracto no borramos el + debito, habria que ver si queremos hacer eso modificando la funcion de + arriba directamente + """ + + check = False + if counterpart_aml_dicts: + for line in counterpart_aml_dicts: + move_line = line.get('move_line') + check = move_line and move_line.payment_id.check_id or False + moves = super(AccountBankStatementLine, self).process_reconciliation( + counterpart_aml_dicts=counterpart_aml_dicts, + payment_aml_rec=payment_aml_rec, new_aml_dicts=new_aml_dicts) + if check and check.state == 'handed': + if check.journal_id != self.statement_id.journal_id: + raise ValidationError(_( + 'To record the debit of a check from the statement,' + ' the check and extract journal must be the same.' ) + ) + if len(moves) != 1: + raise ValidationError(_( + 'To record the debit of a check from the extract ' + 'there should only be one counterpart line.')) + check._add_operation('debited', self, date=self.date) + return moves diff --git a/account_check/models/account_chart_template.py b/account_check/models/account_chart_template.py new file mode 100644 index 00000000..23fe72ce --- /dev/null +++ b/account_check/models/account_chart_template.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import models, api, fields +import logging +_logger = logging.getLogger(__name__) + + +class AccountChartTemplate(models.Model): + _inherit = 'account.chart.template' + + rejected_check_account_id = fields.Many2one( + 'account.account.template', + 'Rejected Check Account', + help='Rejection Checks account, for eg. "Rejected Checks"', + # domain=[('type', 'in', ['other'])], + ) + deferred_check_account_id = fields.Many2one( + 'account.account.template', + 'Deferred Check Account', + help='Deferred Checks account, for eg. "Deferred Checks"', + # domain=[('type', 'in', ['other'])], + ) + holding_check_account_id = fields.Many2one( + 'account.account.template', + 'Holding Check Account', + help='Holding Checks account for third checks, ' + 'for eg. "Holding Checks"', + # domain=[('type', 'in', ['other'])], + ) + + @api.multi + def _load_template( + self, company, code_digits=None, + account_ref=None, taxes_ref=None): + account_ref, taxes_ref = super( + AccountChartTemplate, self)._load_template( + company, + code_digits=code_digits, + account_ref=account_ref, + taxes_ref=taxes_ref) + for field in [ + 'rejected_check_account_id', + 'deferred_check_account_id', + 'holding_check_account_id']: + account_field = self[field] + # TODO we should send it in the context and overwrite with + # lower hierichy values + if account_field: + company[field] = account_ref[account_field.id] + return account_ref, taxes_ref + + @api.multi + def _create_bank_journals(self, company, acc_template_ref): + """ + Bank - Cash journals are created with this method + Inherit this function in order to add checks to cash and bank + journals. This is because usually will be installed before chart loaded + and they will be disable by default + """ + + res = super( + AccountChartTemplate, self)._create_bank_journals( + company, acc_template_ref) + + # creamos diario para cheques de terceros + received_third_check = self.env.ref( + 'account_check.account_payment_method_received_third_check') + delivered_third_check = self.env.ref( + 'account_check.account_payment_method_delivered_third_check') + self.env['account.journal'].create({ + 'name': 'Cheques de Terceros', + 'type': 'cash', + 'company_id': company.id, + 'inbound_payment_method_ids': [ + (4, received_third_check.id, None)], + 'outbound_payment_method_ids': [ + (4, delivered_third_check.id, None)], + }) + + self.env['account.journal'].with_context( + force_company_id=company.id)._enable_issue_check_on_bank_journals() + return res diff --git a/account_check/models/account_check.py b/account_check/models/account_check.py new file mode 100644 index 00000000..d60136b7 --- /dev/null +++ b/account_check/models/account_check.py @@ -0,0 +1,831 @@ +# -*- coding: utf-8 -*- +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import fields, models, _, api +from odoo.exceptions import UserError, ValidationError +import logging +_logger = logging.getLogger(__name__) + + +class AccountCheckOperation(models.Model): + + _name = 'account.check.operation' + _description = 'account.check.operation' + _rec_name = 'operation' + _order = 'date asc, id asc' + # _order = 'create_date desc' + + # al final usamos solo date y no datetime porque el otro dato ya lo tenemos + # en create_date. ademas el orden es una mezcla de la fecha el id + # y entonces la fecha la solemos computar con el payment date para que + # sea igual a la fecha contable (payment date va al asiento) + # date = fields.Datetime( + date = fields.Date( + default=fields.Date.context_today, + # default=lambda self: fields.Datetime.now(), + required=True, + index=True, + ) + check_id = fields.Many2one( + 'account.check', + 'Check', + required=True, + ondelete='cascade', + auto_join=True, + index=True, + ) + operation = fields.Selection([ + # from payments + ('holding', 'Received'), + ('deposited', 'Deposited'), + ('sold', 'Sold'), + ('delivered', 'Delivered'), + # usado para hacer transferencias internas, es lo mismo que delivered + # (endosado) pero no queremos confundir con terminos, a la larga lo + # volvemos a poner en holding + ('transfered', 'Transfered'), + ('handed', 'Handed'), + ('withdrawed', 'Withdrawn'), + # from checks + ('reclaimed', 'Claimed'), + ('rejected', 'Rejected'), + ('debited', 'Debited'), + ('returned', 'Returned'), + # al final no vamos a implemnetar esto ya que habria que hacer muchas + # cosas hasta actualizar el asiento, mejor se vuelve atras y se + # vuelve a generar deuda y listo, igualmente lo dejamos por si se + # quiere usar de manera manual + ('changed', 'Changed'), + ('cancel', 'Cancelled'), + ], + required=True, + index=True, + string='Operation', + ) + origin_name = fields.Char( + compute='_compute_origin_name' + ) + origin = fields.Reference( + string='Origin Document', + selection='_reference_models') + partner_id = fields.Many2one( + 'res.partner', + string='Partner', + ) + notes = fields.Text( + string='Operation Note' + ) + + @api.multi + def unlink(self): + for rec in self: + if rec.origin: + raise ValidationError(_( + 'You can not delete a check operation that has an origin.' + '\nYou can delete the origin reference and unlink after.')) + return super(AccountCheckOperation, self).unlink() + + @api.multi + @api.depends('origin') + def _compute_origin_name(self): + """ + We add this computed method because an error on tree view displaying + reference field when destiny record is deleted. + As said in this post (last answer) we should use name_get instead of + display_name + https://www.odoo.com/es_ES/forum/ayuda-1/question/ + how-to-override-name-get-method-in-new-api-61228 + """ + for rec in self: + try: + if rec.origin: + _id, name = rec.origin.name_get()[0] + origin_name = name + # origin_name = rec.origin.display_name + else: + origin_name = False + except Exception as e: + _logger.exception( + "Compute origin on checks exception: %s" % e) + # if we can get origin we clean it + rec.write({'origin': False}) + origin_name = False + rec.origin_name = origin_name + + @api.model + def _reference_models(self): + return [ + ('account.payment', 'Payment'), + ('account.check', 'Check'), + ('account.invoice', 'Invoice'), + ('account.move', 'Journal Entry'), + ('account.move.line', 'Journal Item'), + ('account.bank.statement.line', 'Statement Line'), + ] + + +class AccountCheck(models.Model): + + _name = 'account.check' + _description = 'Account Check' + _order = "id desc" + _inherit = ['mail.thread', 'mail.activity.mixin'] + + operation_ids = fields.One2many( + 'account.check.operation', + 'check_id', + auto_join=True, + ) + name = fields.Char( + required=True, + readonly=True, + copy=False, + states={'draft': [('readonly', False)]}, + index=True, + ) + number = fields.Integer( + required=True, + readonly=True, + states={'draft': [('readonly', False)]}, + copy=False, + index=True, + ) + checkbook_id = fields.Many2one( + 'account.checkbook', + 'Checkbook', + readonly=True, + states={'draft': [('readonly', False)]}, + auto_join=True, + index=True, + ) + issue_check_subtype = fields.Selection( + related='checkbook_id.issue_check_subtype', + ) + type = fields.Selection( + [('issue_check', 'Issue Check'), ('third_check', 'Third Check'), + ('issue_promissory_note', 'Issue Promissory Note'), ('deposit_promissory_note', 'Deposit Promissory Note')], + readonly=True, + index=True, + ) + partner_id = fields.Many2one( + related='operation_ids.partner_id', + store=True, + index=True, + string='Last operation partner', + ) + first_partner_id = fields.Many2one( + 'res.partner', + compute='_compute_first_partner', + string='First operation partner', + readonly=True, + store=True, + ) + state = fields.Selection([ + ('draft', 'Draft'), + ('holding', 'Holding'), + ('deposited', 'Deposited'), + ('sold', 'Sold'), + ('delivered', 'Delivered'), + ('transfered', 'Transfered'), + ('reclaimed', 'Reclaimed'), + ('withdrawed', 'Withdrawed'), + ('handed', 'Handed'), + ('rejected', 'Rejected'), + ('debited', 'Debited'), + ('returned', 'Returned'), + ('changed', 'Changed'), + ('cancel', 'Cancelled'), + ], + required=True, + default='draft', + copy=False, + compute='_compute_state', + store=True, + index=True, + string="Status" + ) + issue_date = fields.Date( + 'Issue Date', + required=True, + readonly=True, + states={'draft': [('readonly', False)]}, + default=fields.Date.context_today, + ) + owner_vat = fields.Char( + 'Owner Vat', + readonly=True, + states={'draft': [('readonly', False)]} + ) + owner_name = fields.Char( + 'Owner Name', + readonly=True, + states={'draft': [('readonly', False)]} + ) + bank_id = fields.Many2one( + 'res.bank', 'Bank', + readonly=True, + states={'draft': [('readonly', False)]} + ) + amount = fields.Monetary( + currency_field='currency_id', + readonly=True, + states={'draft': [('readonly', False)]} + ) + amount_company_currency = fields.Monetary( + currency_field='company_currency_id', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + currency_id = fields.Many2one( + 'res.currency', + readonly=True, + states={'draft': [('readonly', False)]}, + default=lambda self: self.env.user.company_id.currency_id.id, + required=True, + ) + payment_date = fields.Date( + readonly=True, + states={'draft': [('readonly', False)]}, + index=True, + ) + journal_id = fields.Many2one( + 'account.journal', + string='Journal', + required=True, + domain=[('type', 'in', ['cash', 'bank'])], + readonly=True, + states={'draft': [('readonly', False)]}, + index=True, + ) + company_id = fields.Many2one( + related='journal_id.company_id', + store=True, + ) + company_currency_id = fields.Many2one( + related='company_id.currency_id', + string='Company currency', + ) + + @api.depends('operation_ids.partner_id') + def _compute_first_partner(self): + for rec in self: + rec.first_partner_id = rec.operation_ids and rec.operation_ids[0].partner_id or False + + @api.multi + def onchange(self, values, field_name, field_onchange): + """ + Con esto arreglamos el borrador del origin de una operacíón de deposito + (al menos depositos de v8 migrados), habría que ver si pasa en otros + casos y hay algo más que arreglar + # TODO si no pasa en v11 borrarlo + """ + 'operation_ids.origin' in field_onchange and field_onchange.pop( + 'operation_ids.origin') + return super(AccountCheck, self).onchange( + values, field_name, field_onchange) + + @api.multi + @api.constrains('issue_date', 'payment_date') + @api.onchange('issue_date', 'payment_date') + def onchange_date(self): + for rec in self: + if ( + rec.issue_date and rec.payment_date and + rec.issue_date > rec.payment_date): + raise UserError( + _('Check Payment Date must be greater than Issue Date')) + + @api.multi + @api.constrains( + 'type', + 'number', + ) + def issue_number_interval(self): + for rec in self: + # if not range, then we dont check it + if rec.type == 'issue_check' and rec.checkbook_id.range_to: + if rec.number > rec.checkbook_id.range_to: + raise UserError(_( + "Check number (%s) can't be greater than %s on " + "checkbook %s (%s)") % ( + rec.number, + rec.checkbook_id.range_to, + rec.checkbook_id.name, + rec.checkbook_id.id, + )) + elif rec.number == rec.checkbook_id.range_to: + rec.checkbook_id.state = 'used' + return False + + @api.multi + @api.constrains( + 'type', + 'owner_name', + 'bank_id', + ) + def _check_unique(self): + for rec in self: + if rec.type == 'issue_check': + same_checks = self.search([ + ('checkbook_id', '=', rec.checkbook_id.id), + ('type', '=', rec.type), + ('number', '=', rec.number), + ]) + same_checks -= self + if same_checks: + raise ValidationError(_( + 'Check Number (%s) must be unique per Checkbook!\n' + '* Check ids: %s') % ( + rec.name, same_checks.ids)) + elif self.type == 'third_check': + # agregamos condicion de company ya que un cheque de terceros + # se puede pasar entre distintas cias + same_checks = self.search([ + ('company_id', '=', rec.company_id.id), + ('bank_id', '=', rec.bank_id.id), + ('owner_name', '=', rec.owner_name), + ('type', '=', rec.type), + ('number', '=', rec.number), + ]) + same_checks -= self + if same_checks: + raise ValidationError(_( + 'Check Number (%s) must be unique per Owner and Bank!' + '\n* Check ids: %s') % ( + rec.name, same_checks.ids)) + return True + + @api.multi + def _del_operation(self, origin): + """ + We check that the operation that is being cancel is the last operation + done (same as check state) + """ + for rec in self: + if not rec.operation_ids or rec.operation_ids[-1].origin != origin: + raise ValidationError(_( + 'You can not cancel this operation because this is not ' + 'the last operation over the check.\nCheck (id): %s (%s)' + ) % (rec.name, rec.id)) + rec.operation_ids[-1].origin = False + rec.operation_ids[-1].unlink() + + @api.multi + def _add_operation( + self, operation, origin, partner=None, date=False): + for rec in self: + rec._check_state_change(operation) + # agregamos validacion de fechas + date = date or fields.Datetime.now() + if rec.operation_ids and rec.operation_ids[-1].date > date: + raise ValidationError(_( + 'The date of a new check operation can not be minor than ' + 'last operation date.\n' + '* Check Id: %s\n' + '* Check Number: %s\n' + '* Operation: %s\n' + '* Operation Date: %s\n' + '* Last Operation Date: %s') % ( + rec.id, rec.name, operation, date, + rec.operation_ids[-1].date)) + vals = { + 'operation': operation, + 'date': date, + 'check_id': rec.id, + 'origin': '%s,%i' % (origin._name, origin.id), + 'partner_id': partner and partner.id or False, + } + rec.operation_ids.create(vals) + + @api.multi + @api.depends( + 'operation_ids', + 'operation_ids.operation', + 'operation_ids.date', + ) + def _compute_state(self): + for rec in self: + if rec.operation_ids: + operation = rec.operation_ids[-1].operation + rec.state = operation + else: + rec.state = 'draft' + + @api.multi + def _check_state_change(self, operation): + """ + We only check state change from _add_operation because we want to + leave the user the possibility of making anything from interface. + Necesitamos este chequeo para evitar, por ejemplo, que un cheque se + agregue dos veces en un pago y luego al confirmar se entregue dos veces + On operation_from_state_map dictionary: + * key is 'to state' + * value is 'from states' + """ + self.ensure_one() + # if we do it from _add_operation only, not from a contraint of before + # computing the value, we can just read it + old_state = self.state + operation_from_state_map = { + # 'draft': [False], + 'holding': [ + 'draft', 'deposited', 'sold', 'delivered', 'transfered'], + 'delivered': ['holding'], + 'deposited': ['holding', 'rejected'], + 'sold': ['holding'], + 'handed': ['draft'], + 'transfered': ['holding'], + 'withdrawed': ['draft'], + 'rejected': ['delivered', 'deposited', 'sold', 'handed'], + 'debited': ['handed'], + 'returned': ['handed', 'holding'], + 'changed': ['handed', 'holding'], + 'cancel': ['draft'], + 'reclaimed': ['rejected'], + } + from_states = operation_from_state_map.get(operation) + if not from_states: + raise ValidationError(_( + 'Operation %s not implemented for checks!') % operation) + if old_state not in from_states: + raise ValidationError(_( + 'You can not "%s" a check from state "%s"!\n' + 'Check nbr (id): %s (%s)') % ( + self.operation_ids._fields['operation'].convert_to_export( + operation, self), + self._fields['state'].convert_to_export(old_state, self), + self.name, + self.id)) + + @api.multi + def unlink(self): + for rec in self: + if rec.state not in ('draft', 'cancel'): + raise ValidationError(_( + 'The Check must be in draft state for unlink !')) + return super(AccountCheck, self).unlink() + +# checks operations from checks + + @api.multi + def bank_debit(self): + self.ensure_one() + if self.state in ['handed']: + payment_values = self.get_payment_values(self.journal_id) + payment = self.env['account.payment'].with_context( + default_name=_('Check "%s" debit') % (self.name), + force_account_id=self.company_id._get_check_account( + 'deferred').id, + ).create(payment_values) + self.post_payment_check(payment) + self.handed_reconcile(payment.move_line_ids.mapped('move_id')) + self._add_operation('debited', payment, date=payment.payment_date) + + @api.model + def post_payment_check(self, payment): + """ No usamos post() porque no puede obtener secuencia, hacemos + parecido a los statements donde odoo ya lo genera posteado + """ + # payment.post() + move = payment._create_payment_entry(payment.amount) + payment.write({'state': 'posted', 'move_name': move.name}) + + @api.multi + def handed_reconcile(self, move): + """ + Funcion que por ahora solo intenta conciliar cheques propios entregados + cuando se hace un debito o cuando el proveedor lo rechaza + """ + + self.ensure_one() + debit_account = self.company_id._get_check_account('deferred') + + # conciliamos + if debit_account.reconcile: + operation = self._get_operation('handed') + if operation.origin._name == 'account.payment': + move_lines = operation.origin.move_line_ids + elif operation.origin._name == 'account.move': + move_lines = operation.origin.line_ids + move_lines |= move.line_ids + move_lines = move_lines.filtered( + lambda x: x.account_id == debit_account) + if len(move_lines) != 2: + raise ValidationError(_( + 'We have found more or less than two journal items to ' + 'reconcile with check debit.\n' + '*Journal items: %s') % move_lines.ids) + move_lines.reconcile() + + @api.model + def get_third_check_account(self): + """ + For third checks, if we use a journal only for third checks, we use + accounts on journal, if not we use company account + # TODO la idea es depreciar esto y que si se usa cheques de terceros + se use la misma cuenta que la del diario y no la cuenta configurada en + la cia, lo dejamos por ahora por nosotros y 4 clientes que estan asi + (cro, ncool, bog). + Esto era cuando permitíamos o usabamos diario de efectivo con cash y + cheques + """ + # self.ensure_one() + # desde los pagos, pueden venir mas de un cheque pero para que + # funcione bien, todos los cheques deberian usar la misma cuenta, + # hacemos esa verificación + account = self.env['account.account'] + for rec in self: + credit_account = rec.journal_id.default_credit_account_id + debit_account = rec.journal_id.default_debit_account_id + inbound_methods = rec.journal_id['inbound_payment_method_ids'] + outbound_methods = rec.journal_id['outbound_payment_method_ids'] + # si hay cuenta en diario y son iguales, y si los metodos de pago + # y cobro son solamente uno, usamos el del diario, si no, usamos el + # de la compañía + if credit_account and credit_account == debit_account and len( + inbound_methods) == 1 and len(outbound_methods) == 1: + account |= credit_account + else: + account |= rec.company_id._get_check_account('holding') + if len(account) != 1: + raise ValidationError(_('Error not specified')) + return account + + @api.model + def _get_checks_to_date_on_state(self, state, date, force_domain=None): + """ + Devuelve el listado de cheques que a la fecha definida se encontraban + en el estadao definido. + Esta función no la usamos en este módulo pero si en otros que lo + extienden + La funcion devuelve un listado de las operaciones a traves de las + cuales se puede acceder al cheque, devolvemos las operaciones porque + dan información util de fecha, partner y demas + """ + # buscamos operaciones anteriores a la fecha que definan este estado + if not force_domain: + force_domain = [] + + operations = self.operation_ids.search([ + ('date', '<=', date), + ('operation', '=', state)] + force_domain) + + for operation in operations: + # buscamos si hay alguna otra operacion posterior para el cheque + newer_op = operation.search([ + ('date', '<=', date), + ('id', '>', operation.id), + ('check_id', '=', operation.check_id.id), + ]) + # si hay una operacion posterior borramos la op del cheque porque + # hubo otra operación antes de la fecha + if newer_op: + operations -= operation + return operations + + @api.multi + def _get_operation(self, operation, partner_required=False): + self.ensure_one() + op = self.operation_ids.search([ + ('check_id', '=', self.id), ('operation', '=', operation)], + limit=1) + if partner_required: + if not op.partner_id: + raise ValidationError(_( + 'The %s (id %s) operation has no partner linked.' + 'You will need to do it manually.') % (operation, op.id)) + return op + + @api.multi + def claim(self): + self.ensure_one() + if self.state in ['rejected'] and self.type == 'third_check': + # anulamos la operación en la que lo recibimos + return self.action_create_debit_note( + 'reclaimed', 'customer', self.first_partner_id, + self.company_id._get_check_account('rejected')) + + @api.multi + def customer_return(self): + self.ensure_one() + if self.state in ['holding'] and self.type == 'third_check': + return self.action_create_debit_note( + 'returned', 'customer', self.first_partner_id, + False) + #self.get_third_check_account()) + + @api.model + def get_payment_values(self, journal): + """ return dictionary with the values to create the reject check + payment record. + We create an outbound payment instead of a transfer because: + 1. It is easier to inherit + 2. Outbound payment withot partner type and partner is not seen by user + and we don't want to confuse them with this payments + """ + action_date = self._context.get('action_date', fields.Date.today()) + return { + 'amount': self.amount, + 'currency_id': self.currency_id.id, + 'journal_id': journal.id, + 'payment_date': action_date, + 'payment_type': 'outbound', + 'payment_method_id': journal._default_outbound_payment_methods().id, + # 'check_ids': [(4, self.id, False)], + } + + @api.constrains('currency_id', 'amount', 'amount_company_currency') + def _check_amounts(self): + for rec in self.filtered( + lambda x: not x.amount or not x.amount_company_currency): + if rec.currency_id != rec.company_currency_id: + raise ValidationError(_( + 'If you create a check with different currency thant the ' + 'company currency, you must provide "Amount" and "Amount ' + 'Company Currency"')) + elif not rec.amount: + if not rec.amount_company_currency: + raise ValidationError(_( + 'No puede crear un cheque sin importe')) + rec.amount = rec.amount_company_currency + elif not rec.amount_company_currency: + rec.amount_company_currency = rec.amount + + @api.multi + def reject(self): + self.ensure_one() + if self.state in ['deposited', 'sold']: + operation = self._get_operation(self.state) + if operation.origin._name == 'account.payment': + journal = operation.origin.destination_journal_id + # for compatibility with migration from v8 + elif operation.origin._name == 'account.move': + journal = operation.origin.journal_id + else: + raise ValidationError(_( + 'The deposit operation is not linked to a payment.' + 'If you want to reject you need to do it manually.')) + payment_vals = self.get_payment_values(journal) + payment = self.env['account.payment'].with_context( + default_name=_('Check "%s" rejection') % (self.name), + force_account_id=self.company_id._get_check_account( + 'rejected').id, + ).create(payment_vals) + self.post_payment_check(payment) + self._add_operation('rejected', payment, date=payment.payment_date) + elif self.state == 'delivered': + operation = self._get_operation(self.state, True) + return self.action_create_debit_note( + 'rejected', 'supplier', operation.partner_id, + self.company_id._get_check_account('rejected')) + elif self.state == 'handed': + operation = self._get_operation(self.state, True) + return self.action_create_debit_note( + 'rejected', 'supplier', operation.partner_id, + self.company_id._get_check_account('deferred')) + + @api.multi + def action_create_debit_note(self, operation, partner_type, partner, account): + self.ensure_one() +# action_date = self._context.get('action_date') +# +# if partner_type == 'supplier': +# invoice_type = 'in_invoice' +# journal_type = 'purchase' +# view_id = self.env.ref('account.invoice_supplier_form').id +# else: +# invoice_type = 'out_invoice' +# journal_type = 'sale' +# view_id = self.env.ref('account.invoice_form').id +# +# journal = self.env['account.journal'].search([ +# ('company_id', '=', self.company_id.id), +# ('type', '=', journal_type), +# ], limit=1) +# +# # si pedimos rejected o reclamo, devolvemos mensaje de rechazo y cuenta +# # de rechazo +# if operation in ['rejected', 'reclaimed']: +# name = 'Rechazo cheque "%s"' % (self.name) +# # si pedimos la de holding es una devolucion +# elif operation == 'returned': +# name = 'Devolución cheque "%s"' % (self.name) +# else: +# raise ValidationError(_( +# 'Debit note for operation %s not implemented!' % ( +# operation))) +# +# inv_line_vals = { +# # 'product_id': self.product_id.id, +# 'name': name, +# 'account_id': account.id, +# 'price_unit': self.amount, +# # 'invoice_id': invoice.id, +# } +# +# inv_vals = { +# # this is the reference that goes on account.move.line of debt line +# # 'name': name, +# # this is the reference that goes on account.move +# 'rejected_check_id': self.id, +# 'reference': name, +# 'date_invoice': action_date, +# 'origin': _('Check nbr (id): %s (%s)') % (self.name, self.id), +# 'journal_id': journal.id, +# # this is done on muticompany fix +# # 'company_id': journal.company_id.id, +# 'partner_id': partner.id, +# 'type': invoice_type, +# 'invoice_line_ids': [(0, 0, inv_line_vals)], +# } +# if self.currency_id: +# inv_vals['currency_id'] = self.currency_id.id +# # we send internal_type for compatibility with account_document +# invoice = self.env['account.invoice'].with_context( +# internal_type='debit_note').create(inv_vals) +# self._add_operation(operation, invoice, partner, date=action_date) +# +# return { +# 'name': name, +# 'view_type': 'form', +# 'view_mode': 'form', +# 'res_model': 'account.invoice', +# 'view_id': view_id, +# 'res_id': invoice.id, +# 'type': 'ir.actions.act_window', +# } + + action_date = self._context.get('action_date') + journal = self._context.get('journal_id') + debit_account = self._context.get('debit_account_id') + credit_account = self._context.get('credit_account_id') + + if operation in ['rejected', 'reclaimed'] and self.type == 'third_check': + name = 'Rejected check "%s"' % (self.name) + elif operation == 'returned' and self.type == 'third_check': + name = 'Returned check "%s"' % (self.name) +# elif operation in ['rejected', 'reclaimed'] and self.type == 'deposit_promissory_note': +# name = 'Rejected promissory note "%s"' % (self.name) +# elif operation == 'returned' and self.type == 'third_promissory_note': +# name = 'Returned promissory note "%s"' % (self.name) + else: + raise ValidationError(_( + 'Debit note for operation %s not implemented!' % ( + operation))) + + vals = self.prepare_new_operation_move_values(debit_account, credit_account, name) + vals['journal_id'] = journal.id or False + vals['date'] = action_date + move = self.env['account.move'].create(vals) + move.post() + self._add_operation(operation, move, partner, date=action_date) + + return True + + + @api.multi + def prepare_new_operation_move_values(self, debit_account, credit_account, name, partner=False): + self.ensure_one() + ref = name + amount = self.amount + debit, credit, amount_currency, currency_id = self.env['account.move.line']._compute_amount_fields( + amount, self.currency_id, self.company_currency_id) + if self.company_currency_id != self.currency_id: + currency_id = self.currency_id.id + else: + currency_id = False + amount_currency = False + debit_line_vals = { + 'name': name, + 'debit': debit, + 'credit': credit, + 'partner_id': partner and partner.id or False, + 'account_id': debit_account.id, + 'amount_currency': amount_currency, + 'currency_id': currency_id, + 'ref': ref, + } + credit_line_vals = debit_line_vals.copy() + credit_line_vals['debit'] = debit_line_vals['credit'] + credit_line_vals['credit'] = debit_line_vals['debit'] + credit_line_vals['account_id'] = credit_account.id + credit_line_vals['amount_currency'] = -1 * debit_line_vals['amount_currency'] + + move_vals = { + 'ref': name, + 'line_ids': [ + (0, False, debit_line_vals), + (0, False, credit_line_vals)], + } + return move_vals + + + + + + + + + diff --git a/account_check/models/account_checkbook.py b/account_check/models/account_checkbook.py new file mode 100644 index 00000000..2206e9b1 --- /dev/null +++ b/account_check/models/account_checkbook.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import fields, models, api, _ +import logging +from odoo.exceptions import ValidationError +_logger = logging.getLogger(__name__) + + +class AccountCheckbook(models.Model): + + _name = 'account.checkbook' + _description = 'Account Checkbook' + + name = fields.Char( + compute='_compute_name', + ) + sequence_id = fields.Many2one( + 'ir.sequence', + 'Sequence', + copy=False, + domain=[('code', '=', 'issue_check')], + help="Checks numbering sequence.", + context={'default_code': 'issue_check'}, + ) + next_number = fields.Integer( + 'Next Number', + # usamos compute y no related para poder usar sudo cuando se setea + # secuencia sin necesidad de dar permiso en ir.sequence + compute='_compute_next_number', + inverse='_inverse_next_number', + ) + issue_check_subtype = fields.Selection( + [('deferred', 'Deferred'), ('currents', 'Currents')], + string='Issue Check Subtype', + required=True, + default='deferred', + help='* Con cheques corrientes el asiento generado por el pago ' + 'descontará directamente de la cuenta de banco y además la fecha de ' + 'pago no es obligatoria.\n' + '* Con cheques diferidos el asiento generado por el pago se hará ' + 'contra la cuenta definida para tal fin en la compañía, luego será ' + 'necesario el asiento de débito que se puede generar desde el extracto' + ' o desde el cheque.', + ) + journal_id = fields.Many2one( + 'account.journal', 'Journal', + help='Journal where it is going to be used', + readonly=True, + required=True, + domain=[('type', '=', 'bank')], + ondelete='cascade', + context={'default_type': 'bank'}, + states={'draft': [('readonly', False)]}, + auto_join=True, + ) + range_to = fields.Integer( + 'To Number', + # readonly=True, + # states={'draft': [('readonly', False)]}, + help='If you set a number here, this checkbook will be automatically' + ' set as used when this number is raised.' + ) + issue_check_ids = fields.One2many( + 'account.check', + 'checkbook_id', + string='Issue Checks', + readonly=True, + ) + state = fields.Selection( + [('draft', 'Draft'), ('active', 'In Use'), ('used', 'Used')], + string='Status', + # readonly=True, + default='draft', + copy=False, + ) + # TODO depreciar esta funcionalidad que no estamos usando + block_manual_number = fields.Boolean( + default=True, + string='Block manual number?', + # readonly=True, + # states={'draft': [('readonly', False)]}, + help='Block user to enter manually another number than the suggested' + ) + numerate_on_printing = fields.Boolean( + default=False, + string='Numerate on printing?', + # readonly=True, + # states={'draft': [('readonly', False)]}, + help='No number will be assigne while creating payment, number will be' + 'assigned after printing check.' + ) + report_template = fields.Many2one( + 'ir.actions.report', + 'Report', + domain="[('model', '=', 'account.payment')]", + context="{'default_model': 'account.payment'}", + help='Report to use when printing checks. If not report selected, ' + 'report with name "check_report" will be used', + ) + + @api.multi + @api.depends('sequence_id.number_next_actual') + def _compute_next_number(self): + for rec in self: + rec.next_number = rec.sequence_id.number_next_actual + + @api.multi + def _inverse_next_number(self): + for rec in self.filtered('sequence_id'): + rec.sequence_id.sudo().number_next_actual = rec.next_number + + @api.model + def create(self, vals): + rec = super(AccountCheckbook, self).create(vals) + if not rec.sequence_id: + rec._create_sequence(vals.get('next_number', 0)) + return rec + + @api.multi + def _create_sequence(self, next_number): + """ Create a check sequence for the checkbook """ + for rec in self: + rec.sequence_id = rec.env['ir.sequence'].sudo().create({ + 'name': '%s - %s' % (rec.journal_id.name, rec.name), + 'implementation': 'no_gap', + 'padding': 8, + 'number_increment': 1, + 'code': 'issue_check', + # si no lo pasamos, en la creacion se setea 1 + 'number_next_actual': next_number, + 'company_id': rec.journal_id.company_id.id, + }) + + @api.multi + def _compute_name(self): + for rec in self: + if rec.issue_check_subtype == 'deferred': + name = _('Deferred Checks') + else: + name = _('Currents Checks') + if rec.range_to: + name += _(' up to %s') % rec.range_to + rec.name = name + + @api.multi + def unlink(self): + if self.mapped('issue_check_ids'): + raise ValidationError( + _('You can drop a checkbook if it has been used on checks!')) + return super(AccountCheckbook, self).unlink() diff --git a/account_check/models/account_invoice.py b/account_check/models/account_invoice.py new file mode 100644 index 00000000..9426c3bf --- /dev/null +++ b/account_check/models/account_invoice.py @@ -0,0 +1,49 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import models, fields, api + + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + # we add this field so that when invoice is validated we can reconcile + # move lines between check and invoice lines + # igual se setea para todos los rechazos, tal vez mas adelante lo usamos + # para otra cosa + rejected_check_id = fields.Many2one( + 'account.check', + 'Rejected Check', + ) + + @api.multi + def action_cancel(self): + """ + Si al cancelar la factura la misma estaba vinculada a un rechazo + intentamos romper la conciliacion del rechazo + """ + for rec in self.filtered(lambda x: x.rejected_check_id): + check = rec.rejected_check_id + deferred_account = check.company_id._get_check_account('deferred') + if ( + check.state == 'rejected' and + check.type == 'issue_check' and + deferred_account.reconcile): + deferred_account_line = rec.move_id.line_ids.filtered( + lambda x: x.account_id == deferred_account) + deferred_account_line.remove_move_reconcile() + return super(AccountInvoice, self).action_cancel() + + @api.multi + def action_move_create(self): + """ + Si al validar la factura, la misma tiene un cheque de rechazo asociado + intentamos concilarlo + """ + res = super(AccountInvoice, self).action_move_create() + for rec in self.filtered(lambda x: x.rejected_check_id): + check = rec.rejected_check_id + if check.state == 'rejected' and check.type == 'issue_check': + rec.rejected_check_id.handed_reconcile(rec.move_id) + return res diff --git a/account_check/models/account_journal.py b/account_check/models/account_journal.py new file mode 100644 index 00000000..63c299ca --- /dev/null +++ b/account_check/models/account_journal.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import models, fields, api, _ +from odoo.tools.misc import formatLang +from ast import literal_eval + + +class AccountJournal(models.Model): + _inherit = 'account.journal' + + checkbook_ids = fields.One2many( + 'account.checkbook', + 'journal_id', + 'Checkbooks', + auto_join=True, + ) + + @api.model + def create(self, vals): + rec = super(AccountJournal, self).create(vals) + issue_checks = self.env.ref( + 'account_check.account_payment_method_issue_check') + if (issue_checks in rec.outbound_payment_method_ids and + not rec.checkbook_ids): + rec._create_checkbook() + return rec + + @api.multi + def _create_checkbook(self): + """ Create a check sequence for the journal """ + for rec in self: + checkbook = rec.checkbook_ids.create({ + 'journal_id': rec.id, + }) + checkbook.state = 'active' + + @api.model + def _enable_issue_check_on_bank_journals(self): + """ Enables issue checks payment method + Called upon module installation via data file. + """ + issue_checks = self.env.ref( + 'account_check.account_payment_method_issue_check') + domain = [('type', '=', 'bank')] + force_company_id = self._context.get('force_company_id') + if force_company_id: + domain += [('company_id', '=', force_company_id)] + bank_journals = self.search(domain) + for bank_journal in bank_journals: + if not bank_journal.checkbook_ids: + bank_journal._create_checkbook() + bank_journal.write({ + 'outbound_payment_method_ids': [(4, issue_checks.id, None)], + }) + +############### +# For dashboard +############### + + @api.multi + def get_journal_dashboard_datas(self): + domain_holding_third_checks = [ + ('type', '=', 'third_check'), + ('journal_id', '=', self.id), + ('state', '=', 'holding') + ] + domain_handed_issue_checks = [ + ('type', '=', 'issue_check'), + ('journal_id', '=', self.id), + ('state', '=', 'handed') + ] + handed_checks = self.env['account.check'].search( + domain_handed_issue_checks) + holding_checks = self.env['account.check'].search( + domain_holding_third_checks) + + num_checks_to_numerate = False + if self.env['ir.actions.report'].search( + [('report_name', '=', 'check_report')]): + num_checks_to_numerate = self.env['account.payment'].search_count([ + ('journal_id', '=', self.id), + ('payment_method_id.code', '=', 'issue_check'), + ('state', '=', 'draft'), + ('check_name', '=', False), + ]) + return dict( + super(AccountJournal, self).get_journal_dashboard_datas(), + num_checks_to_numerate=num_checks_to_numerate, + num_holding_third_checks=len(holding_checks), + show_third_checks=( + 'received_third_check' in + self.inbound_payment_method_ids.mapped('code')), + show_issue_checks=( + 'issue_check' in + self.outbound_payment_method_ids.mapped('code')), + num_handed_issue_checks=len(handed_checks), + handed_amount=formatLang( + self.env, sum(handed_checks.mapped('amount_company_currency')), + currency_obj=self.company_id.currency_id), + holding_amount=formatLang( + self.env, sum(holding_checks.mapped( + 'amount_company_currency')), + currency_obj=self.company_id.currency_id), + ) + + @api.multi + def open_action_checks(self): + check_type = self.env.context.get('check_type', False) + if check_type == 'third_check': + action_name = 'account_check.action_third_check' + elif check_type == 'issue_check': + action_name = 'account_check.action_issue_check' + else: + return False + actions = self.env.ref(action_name) + action_read = actions.read()[0] + context = literal_eval(action_read['context']) + context['search_default_journal_id'] = self.id + action_read['context'] = context + return action_read + + @api.multi + def action_checks_to_numerate(self): + return { + 'name': _('Checks to Print and Numerate'), + 'type': 'ir.actions.act_window', + 'view_mode': 'list,form,graph', + 'res_model': 'account.payment', + 'context': dict( + self.env.context, + search_default_checks_to_numerate=1, + search_default_journal_id=self.id, + journal_id=self.id, + default_journal_id=self.id, + default_payment_type='outbound', + default_payment_method_id=self.env.ref( + 'account_check.account_payment_method_issue_check').id, + ), + } diff --git a/account_check/models/account_payment.py b/account_check/models/account_payment.py new file mode 100644 index 00000000..ef508014 --- /dev/null +++ b/account_check/models/account_payment.py @@ -0,0 +1,627 @@ +# -*- coding: utf-8 -*- +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import fields, models, _, api +from odoo.exceptions import UserError, ValidationError +import logging +# import odoo.addons.decimal_precision as dp +_logger = logging.getLogger(__name__) + + +class AccountPayment(models.Model): + + _inherit = 'account.payment' + + check_ids = fields.Many2many( + 'account.check', + string='Checks', + copy=False, + readonly=True, + states={'draft': [('readonly', False)]}, + auto_join=True, + ) + # we add this field for better usability on issue checks and received + # checks. We keep m2m field for backward compatibility where we allow to + # use more than one check per payment + check_id = fields.Many2one( + 'account.check', + compute='_compute_check', + string='Check', + ) + check_deposit_type = fields.Selection( + [('consolidated', 'Consolidated'), + ('detailed', 'Detailed')], + default='detailed', + help="This option is relevant if you use bank statements. Detailed is" + " used when the bank credits one by one the checks, consolidated is" + " for when the bank credits all the checks in a single movement", + ) + + @api.multi + @api.depends('check_ids') + def _compute_check(self): + for rec in self: + # we only show checks for issue checks or received thid checks + # if len of checks is 1 + if rec.payment_method_code in ( + 'received_third_check', + 'issue_check',) and len(rec.check_ids) == 1: + rec.check_id = rec.check_ids[0].id + else: + rec.check_id = False + +# check fields, just to make it easy to load checks without need to create +# them by a m2o record + check_name = fields.Char( + 'Check Name', + readonly=True, + copy=False, + states={'draft': [('readonly', False)]}, + ) + check_number = fields.Integer( + 'Check Number', + readonly=True, + states={'draft': [('readonly', False)]}, + copy=False, + ) + check_issue_date = fields.Date( + 'Check Issue Date', + readonly=True, + copy=False, + states={'draft': [('readonly', False)]}, + default=fields.Date.context_today, + ) + check_payment_date = fields.Date( + 'Check Payment Date', + readonly=True, + help="Only if this check is post dated", + states={'draft': [('readonly', False)]}, + ) + checkbook_id = fields.Many2one( + 'account.checkbook', + 'Checkbook', + readonly=True, + states={'draft': [('readonly', False)]}, + auto_join=True, + ) + check_subtype = fields.Selection( + related='checkbook_id.issue_check_subtype', + ) + check_bank_id = fields.Many2one( + 'res.bank', + 'Check Bank', + readonly=True, + copy=False, + states={'draft': [('readonly', False)]}, + auto_join=True, + ) + check_owner_vat = fields.Char( + 'Check Owner Vat', + readonly=True, + copy=False, + states={'draft': [('readonly', False)]} + ) + check_owner_name = fields.Char( + 'Check Owner Name', + readonly=True, + copy=False, + states={'draft': [('readonly', False)]} + ) + # this fields is to help with code and view + check_type = fields.Char( + compute='_compute_check_type', + ) + checkbook_numerate_on_printing = fields.Boolean( + related='checkbook_id.numerate_on_printing', + ) + # TODO borrar, esto estaria depreciado + # checkbook_block_manual_number = fields.Boolean( + # related='checkbook_id.block_manual_number', + # readonly=True, + # ) + # check_number_readonly = fields.Integer( + # related='check_number', + # readonly=True, + # ) + + @api.multi + @api.depends('payment_method_code') + def _compute_check_type(self): + for rec in self: + if rec.payment_method_code == 'issue_check': + rec.check_type = 'issue_check' + elif rec.payment_method_code in [ + 'received_third_check', + 'delivered_third_check']: + rec.check_type = 'third_check' + else: + rec.check_type = False + + @api.multi + def _compute_payment_method_description(self): + check_payments = self.filtered( + lambda x: x.payment_method_code in + ['issue_check', 'received_third_check', 'delivered_third_check']) + for rec in check_payments: + if rec.check_ids: + checks_desc = ', '.join(rec.check_ids.mapped('name')) + else: + checks_desc = rec.check_name + name = "%s: %s" % (rec.payment_method_id.display_name, checks_desc) + rec.payment_method_description = name + return super( + AccountPayment, + (self - check_payments))._compute_payment_method_description() + +# on change methods + + @api.constrains('check_ids') + @api.onchange('check_ids', 'payment_method_code') + def onchange_checks(self): + for rec in self: + # we only overwrite if payment method is delivered + if rec.payment_method_code == 'delivered_third_check': + rec.amount = sum(rec.check_ids.mapped('amount')) + currency = rec.check_ids.mapped('currency_id') + + if len(currency) > 1: + raise ValidationError(_( + 'You are trying to deposit checks of difference' + ' currencies, this functionality is not supported')) + elif len(currency) == 1: + rec.currency_id = currency.id + + # si es una entrega de cheques de terceros y es en otra moneda + # a la de la cia, forzamos el importe en moneda de cia de los + # cheques originales + # escribimos force_amount_company_currency directamente en vez + # de amount_company_currency por lo explicado en + # _inverse_amount_company_currency + if rec.currency_id != rec.company_id.currency_id: + rec.force_amount_company_currency = sum( + rec.check_ids.mapped('amount_company_currency')) + + @api.multi + @api.onchange('check_number') + def change_check_number(self): + # TODO make default padding a parameter + def _get_name_from_number(number): + padding = 8 + if len(str(number)) > padding: + padding = len(str(number)) + return ('%%0%sd' % padding % number) + + for rec in self: + if rec.payment_method_code in ['received_third_check']: + if not rec.check_number: + check_name = False + else: + check_name = _get_name_from_number(rec.check_number) + rec.check_name = check_name + elif rec.payment_method_code in ['issue_check']: + sequence = rec.checkbook_id.sequence_id + if not rec.check_number: + check_name = False + elif sequence: + if rec.check_number != sequence.number_next_actual: + # write with sudo for access rights over sequence + sequence.sudo().write( + {'number_next_actual': rec.check_number}) + check_name = rec.checkbook_id.sequence_id.next_by_id() + else: + # in sipreco, for eg, no sequence on checkbooks + check_name = _get_name_from_number(rec.check_number) + rec.check_name = check_name + + @api.onchange('check_issue_date', 'check_payment_date') + def onchange_date(self): + if ( + self.check_issue_date and self.check_payment_date and + self.check_issue_date > self.check_payment_date): + self.check_payment_date = False + raise UserError( + _('Check Payment Date must be greater than Issue Date')) + + @api.onchange('check_owner_vat') + def onchange_check_owner_vat(self): + """ + We suggest owner name from owner vat + """ + # if not self.check_owner_name: + self.check_owner_name = self.search( + [('check_owner_vat', '=', self.check_owner_vat)], + limit=1).check_owner_name + + @api.onchange('partner_id', 'payment_method_code') + def onchange_partner_check(self): + commercial_partner = self.partner_id.commercial_partner_id + if self.payment_method_code == 'received_third_check': + self.check_bank_id = ( + commercial_partner.bank_ids and + commercial_partner.bank_ids[0].bank_id or False) + # en realidad se termina pisando con onchange_check_owner_vat + # entonces llevamos nombre solo si ya existe la priemr vez + # TODO ver si lo mejoramos o borramos esto directamente + # self.check_owner_name = commercial_partner.name + vat_field = 'vat' + # to avoid needed of another module, we add this check to see + # if l10n_ar cuit field is available + if 'cuit' in commercial_partner._fields: + vat_field = 'cuit' + self.check_owner_vat = commercial_partner[vat_field] + elif self.payment_method_code == 'issue_check': + self.check_bank_id = self.journal_id.bank_id + self.check_owner_name = False + self.check_owner_vat = False + # no hace falta else porque no se usa en otros casos + + @api.onchange('payment_method_code') + def _onchange_payment_method_code(self): + if self.payment_method_code == 'issue_check': + checkbook = self.env['account.checkbook'].search([ + ('state', '=', 'active'), + ('journal_id', '=', self.journal_id.id)], + limit=1) + self.checkbook_id = checkbook + elif self.checkbook_id: + # TODO ver si interesa implementar volver atras numeracion + self.checkbook_id = False + # si cambiamos metodo de pago queremos refrescar listado de cheques + # seleccionados + self.check_ids = False + + @api.onchange('checkbook_id') + def onchange_checkbook(self): + if self.checkbook_id and not self.checkbook_id.numerate_on_printing: + self.check_number = self.checkbook_id.next_number + else: + self.check_number = False + +# post methods + @api.multi + def cancel(self): + for rec in self: + # solo cancelar operaciones si estaba postead, por ej para comp. + # con pagos confirmados, se cancelan pero no hay que deshacer nada + # de asientos ni cheques + if rec.state in ['confirmed', 'posted']: + rec.do_checks_operations(cancel=True) + res = super(AccountPayment, self).cancel() + return res + + @api.multi + def create_check(self, check_type, operation, bank): + self.ensure_one() + + check_vals = { + 'bank_id': bank.id, + 'owner_name': self.check_owner_name, + 'owner_vat': self.check_owner_vat, + 'number': self.check_number, + 'name': self.check_name, + 'checkbook_id': self.checkbook_id.id, + 'issue_date': self.check_issue_date, + 'type': self.check_type, + 'journal_id': self.journal_id.id, + 'amount': self.amount, + 'payment_date': self.check_payment_date, + 'currency_id': self.currency_id.id, + } + + check = self.env['account.check'].create(check_vals) + self.check_ids = [(4, check.id, False)] + check._add_operation( + operation, self, self.partner_id, date=self.payment_date) + return check + + @api.multi + def do_checks_operations(self, vals=None, cancel=False): + self.ensure_one() + rec = self + if not rec.check_type: + return vals + if ( + rec.payment_method_code == 'received_third_check' and + rec.payment_type == 'inbound' + # el chequeo de partner type no seria necesario + # un proveedor nos podria devolver plata con un cheque + # and rec.partner_type == 'customer' + ): + operation = 'holding' + if cancel: + _logger.info('Cancelled Received Check') + rec.check_ids._del_operation(self) + rec.check_ids.unlink() + return None + + _logger.info('Receive Check') + check = self.create_check( + 'third_check', operation, self.check_bank_id) + vals['date_maturity'] = self.check_payment_date + vals['account_id'] = check.get_third_check_account().id + vals['name'] = _('Receive check %s') % check.name + elif ( + rec.payment_method_code == 'delivered_third_check' and + rec.payment_type == 'transfer'): + # si el cheque es entregado en una transferencia tenemos tres + # opciones + # TODO we should make this method selectable for transfers + inbound_method = ( + rec.destination_journal_id.inbound_payment_method_ids) + # si un solo inbound method y es received third check + # entonces consideramos que se esta moviendo el cheque de un diario + # al otro + if len(inbound_method) == 1 and ( + inbound_method.code == 'received_third_check'): + if cancel: + _logger.info('Cancelled Transfered Check') + for check in rec.check_ids: + check._del_operation(self) + check._del_operation(self) + receive_op = check._get_operation('holding') + if receive_op.origin._name == 'account.payment': + check.journal_id = receive_op.origin.journal_id.id + return None + + _logger.info('Transfered Check') + # get the account before changing the journal on the check + vals['account_id'] = rec.check_ids.get_third_check_account().id + rec.check_ids._add_operation( + 'transfered', rec, False, date=rec.payment_date) + rec.check_ids._add_operation( + 'holding', rec, False, date=rec.payment_date) + rec.check_ids.write({ + 'journal_id': rec.destination_journal_id.id}) + vals['name'] = _('Transfer checks %s') % ', '.join( + rec.check_ids.mapped('name')) + elif rec.destination_journal_id.type == 'cash': + if cancel: + _logger.info('Cancelled Sold Check') + rec.check_ids._del_operation(self) + return None + + _logger.info('Sold Check') + rec.check_ids._add_operation( + 'sold', rec, False, date=rec.payment_date) + vals['account_id'] = rec.check_ids.get_third_check_account().id + vals['name'] = _('Sell check %s') % ', '.join( + rec.check_ids.mapped('name')) + # bank + else: + if cancel: + _logger.info('Cancelled Deposited Check') + rec.check_ids._del_operation(self) + return None + + _logger.info('Deposited Check') + rec.check_ids._add_operation( + 'deposited', rec, False, date=rec.payment_date) + vals['account_id'] = rec.check_ids.get_third_check_account().id + vals['name'] = _('Deposit checks %s') % ', '.join( + rec.check_ids.mapped('name')) + elif ( + rec.payment_method_code == 'delivered_third_check' and + rec.payment_type == 'outbound' + # el chequeo del partner type no es necesario + # podriamos entregarlo a un cliente + # and rec.partner_type == 'supplier' + ): + if cancel: + _logger.info('Cancelled Delivered Check') + rec.check_ids._del_operation(self) + return None + + _logger.info('Delivered Check') + rec.check_ids._add_operation( + 'delivered', rec, rec.partner_id, date=rec.payment_date) + vals['account_id'] = rec.check_ids.get_third_check_account().id + vals['name'] = _('Deliver checks %s') % ', '.join( + rec.check_ids.mapped('name')) + elif ( + rec.payment_method_code == 'issue_check' and + rec.payment_type == 'outbound' + # el chequeo del partner type no es necesario + # podriamos entregarlo a un cliente + # and rec.partner_type == 'supplier' + ): + if cancel: + _logger.info('Cancelled Hand/debit Check') + rec.check_ids._del_operation(self) + rec.check_ids.unlink() + return None + + _logger.info('Hand/debit Check') + # if check is deferred, hand it and later debit it change account + # if check is current, debit it directly + # operation = 'debited' + # al final por ahora depreciamos esto ya que deberiamos adaptar + # rechazos y demas, deferred solamente sin fecha pero con cuenta + # puente + # if self.check_subtype == 'deferred': + vals['account_id'] = self.company_id._get_check_account( + 'deferred').id + operation = 'handed' + check = self.create_check( + 'issue_check', operation, self.check_bank_id) + vals['date_maturity'] = self.check_payment_date + vals['name'] = _('Hand check %s') % check.name + elif ( + rec.payment_method_code == 'issue_check' and + rec.payment_type == 'transfer' and + rec.destination_journal_id.type == 'cash'): + if cancel: + _logger.info('Cancelled Withdrawn Check') + rec.check_ids._del_operation(self) + rec.check_ids.unlink() + return None + + _logger.info('Withdrawn Check') + self.create_check('issue_check', 'withdrawed', self.check_bank_id) + vals['name'] = _('Withdraw with checks %s') % ', '.join( + rec.check_ids.mapped('name')) + vals['date_maturity'] = self.check_payment_date + # if check is deferred, change account + # si retiramos por caja directamente lo sacamos de banco + # if self.check_subtype == 'deferred': + # vals['account_id'] = self.company_id._get_check_account( + # 'deferred').id + else: + raise UserError(_( + 'This operatios is not implemented for checks:\n' + '* Payment type: %s\n' + '* Partner type: %s\n' + '* Payment method: %s\n' + '* Destination journal: %s\n' % ( + rec.payment_type, + rec.partner_type, + rec.payment_method_code, + rec.destination_journal_id.type))) + return vals + + @api.multi + def post(self): + for rec in self: + if rec.check_ids and not rec.currency_id.is_zero( + sum(rec.check_ids.mapped('amount')) - rec.amount): + raise UserError(_( + 'The total of the payment does not match the total of the selected checks. ' + 'Please try deleting or re-adding a check.')) + if rec.payment_method_code == 'issue_check' and (not rec.check_number or not rec.check_name): + raise UserError(_('Please be sure that check number or name is filled!')) + + res = super(AccountPayment, self).post() + return res + + def _get_liquidity_move_line_vals(self, amount): + vals = super(AccountPayment, self)._get_liquidity_move_line_vals( + amount) + vals = self.do_checks_operations(vals=vals) + return vals + + @api.multi + def do_print_checks(self): + # si cambiamos nombre de check_report tener en cuenta en sipreco + checkbook = self.mapped('checkbook_id') + # si todos los cheques son de la misma chequera entonces buscamos + # reporte específico para esa chequera + report_name = len(checkbook) == 1 and \ + checkbook.report_template.report_name \ + or 'check_report' + check_report = self.env['ir.actions.report'].search( + [('report_name', '=', report_name)], limit=1).report_action(self) + # ya el buscar el reporte da el error solo + # if not check_report: + # raise UserError(_( + # "There is no check report configured.\nMake sure to configure " + # "a check report named 'account_check_report'.")) + return check_report + + @api.multi + def print_checks(self): + if len(self.mapped('checkbook_id')) != 1: + raise UserError(_( + "In order to print multiple checks at once, they must belong " + "to the same checkbook.")) + # por ahora preferimos no postearlos + # self.filtered(lambda r: r.state == 'draft').post() + + # si numerar al imprimir entonces llamamos al wizard + if self[0].checkbook_id.numerate_on_printing: + if all([not x.check_name for x in self]): + next_check_number = self[0].checkbook_id.next_number + return { + 'name': _('Print Pre-numbered Checks'), + 'type': 'ir.actions.act_window', + 'res_model': 'print.prenumbered.checks', + 'view_type': 'form', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'payment_ids': self.ids, + 'default_next_check_number': next_check_number, + } + } + # si ya están enumerados mandamos a imprimir directamente + elif all([x.check_name for x in self]): + return self.do_print_checks() + else: + raise UserError(_( + 'Está queriendo imprimir y enumerar cheques que ya han ' + 'sido numerados. Seleccione solo cheques numerados o solo' + ' cheques sin número.')) + else: + return self.do_print_checks() + + def _get_counterpart_move_line_vals(self, invoice=False): + vals = super(AccountPayment, self)._get_counterpart_move_line_vals( + invoice=invoice) + force_account_id = self._context.get('force_account_id') + if force_account_id: + vals['account_id'] = force_account_id + return vals + + @api.multi + def _split_aml_line_per_check(self, move): + """ Take an account mvoe, find the move lines related to check and + split them one per earch check related to the payment + """ + self.ensure_one() + res = self.env['account.move.line'] + move.button_cancel() + checks = self.check_ids + aml = move.line_ids.with_context(check_move_validity=False).filtered( + lambda x: x.name != self.name) + if len(aml) > 1: + raise UserError( + _('Seems like this move has been already splited')) + elif len(aml) == 0: + raise UserError( + _('There is not move lines to split')) + + amount_field = 'credit' if aml.credit else 'debit' + new_name = _('Deposit check %s') if aml.credit else \ + aml.name + _(' check %s') + + # if the move line has currency then we are delivering checks on a + # different currency than company one + currency = aml.currency_id + currency_sign = amount_field == 'debit' and 1.0 or -1.0 + aml.write({ + 'name': new_name % checks[0].name, + amount_field: checks[0].amount_company_currency, + 'amount_currency': currency and currency_sign * checks[0].amount, + }) + res |= aml + checks -= checks[0] + for check in checks: + res |= aml.copy({ + 'name': new_name % check.name, + amount_field: check.amount_company_currency, + 'payment_id': self.id, + 'amount_currency': currency and currency_sign * check.amount, + }) + move.post() + return res + + @api.multi + def _create_payment_entry(self, amount): + move = super(AccountPayment, self)._create_payment_entry(amount) + if self.filtered( + lambda x: x.payment_type == 'transfer' and + x.payment_method_code == 'delivered_third_check' and + x.check_deposit_type == 'detailed'): + self._split_aml_line_per_check(move) + return move + + @api.multi + def _create_transfer_entry(self, amount): + transfer_debit_aml = super( + AccountPayment, self)._create_transfer_entry(amount) + if self.filtered( + lambda x: x.payment_type == 'transfer' and + x.payment_method_code == 'delivered_third_check' and + x.check_deposit_type == 'detailed'): + self._split_aml_line_per_check(transfer_debit_aml.move_id) + return transfer_debit_aml diff --git a/account_check/models/res_company.py b/account_check/models/res_company.py new file mode 100644 index 00000000..f9007103 --- /dev/null +++ b/account_check/models/res_company.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import fields, models, api, _ +from odoo.exceptions import UserError +import logging +_logger = logging.getLogger(__name__) + + +class ResCompany(models.Model): + _inherit = 'res.company' + + rejected_check_account_id = fields.Many2one( + 'account.account', + 'Rejected Checks Account', + help='Rejection Checks account, for eg. "Rejected Checks"', + ) + deferred_check_account_id = fields.Many2one( + 'account.account', + 'Deferred Checks Account', + help='Deferred Checks account, for eg. "Deferred Checks"', + ) + holding_check_account_id = fields.Many2one( + 'account.account', + 'Holding Checks Account', + help='Holding Checks account for third checks, ' + 'for eg. "Holding Checks"', + ) + + @api.multi + def _get_check_account(self, check_type): + self.ensure_one() + if check_type == 'holding': + account = self.holding_check_account_id + elif check_type == 'rejected': + account = self.rejected_check_account_id + elif check_type == 'deferred': + account = self.deferred_check_account_id + else: + raise UserError(_("Check type %s not implemented!") % check_type) + if not account: + raise UserError(_( + 'No checks %s account defined for company %s' + ) % (check_type, self.name)) + return account diff --git a/account_check/models/res_config_settings.py b/account_check/models/res_config_settings.py new file mode 100644 index 00000000..08de3cbb --- /dev/null +++ b/account_check/models/res_config_settings.py @@ -0,0 +1,19 @@ +from odoo import fields, models +# from odoo.exceptions import UserError + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + rejected_check_account_id = fields.Many2one( + related='company_id.rejected_check_account_id', + readonly=False, + ) + deferred_check_account_id = fields.Many2one( + related='company_id.deferred_check_account_id', + readonly=False, + ) + holding_check_account_id = fields.Many2one( + related='company_id.holding_check_account_id', + readonly=False, + ) diff --git a/account_check/security/account_check_security.xml b/account_check/security/account_check_security.xml new file mode 100644 index 00000000..4754e95d --- /dev/null +++ b/account_check/security/account_check_security.xml @@ -0,0 +1,18 @@ + + + + + Check Multi-Company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + + Check Operation Multi-Company + + + ['|',('check_id.company_id','=',False),('check_id.company_id','child_of',[user.company_id.id])] + + + diff --git a/account_check/security/ir.model.access.csv b/account_check/security/ir.model.access.csv new file mode 100644 index 00000000..d87cc722 --- /dev/null +++ b/account_check/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +account_check_access_full,account_check_access_full,model_account_check,account.group_account_invoice,1,1,1,1 +account_checkbook_access_full,account_checkbook_access_full,model_account_checkbook,account.group_account_invoice,1,1,1,1 +account_check_access_global,account_check_access_global,model_account_check,,1,0,0,0 +account_checkbook_access_global,account_checkbook_access_global,model_account_checkbook,,1,0,0,0 +account_check_operation_access_global,account_check_operation_access_global,model_account_check_operation,,1,0,0,0 +account_check_operation_access_full,account_check_operation_access_full,model_account_check_operation,account.group_account_invoice,1,1,1,1 diff --git a/account_check/views/account_chart_template_view.xml b/account_check/views/account_chart_template_view.xml new file mode 100644 index 00000000..b9a76a15 --- /dev/null +++ b/account_check/views/account_chart_template_view.xml @@ -0,0 +1,15 @@ + + + + account.chart.template.form + account.chart.template + + + + + + + + + + diff --git a/account_check/views/account_check_view.xml b/account_check/views/account_check_view.xml new file mode 100644 index 00000000..2c89ed4e --- /dev/null +++ b/account_check/views/account_check_view.xml @@ -0,0 +1,266 @@ + + + + + + account.check.tree + account.check + 100 + + + + + + + + + + + + + + + + + + + + + + + account.check.create.tree + account.check + + + + + true + + + + + + account.check.form + account.check + +

+ + + + + +
+ +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + + account.check.create.form + account.check + + + +
+ true +
+ + + + + + + 0 + {'readonly':[('id','=',False)]} + +
+
+ + + check.search + account.check + + + + + + + + + + + + + + + + + + + + + + + + + + + + + account.check.calendar + account.check + + + + + + + + + account.check.graph + account.check + + + + + + + + + + account.check.calendar + account.check + + + + + + + + + + + + + Third Checks + account.check + form + tree,form,calendar,graph,pivot + [('type','=','third_check')] + {'default_type':'third_check'} + + + + + + + Issue Checks + account.check + form + tree,form,calendar,graph,pivot + [('type','=','issue_check')] + {'default_type':'issue_check'} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/account_check/views/account_checkbook_view.xml b/account_check/views/account_checkbook_view.xml new file mode 100644 index 00000000..84ca92c6 --- /dev/null +++ b/account_check/views/account_checkbook_view.xml @@ -0,0 +1,44 @@ + + + + + account.checkbook.tree + account.checkbook + + + + + + + + + + + + + + + account.checkbook.form + account.checkbook + +
+
+ +
+ + + + + + + + + + + + +
+
+
+ +
diff --git a/account_check/views/account_journal_dashboard_view.xml b/account_check/views/account_journal_dashboard_view.xml new file mode 100644 index 00000000..aef48ba9 --- /dev/null +++ b/account_check/views/account_journal_dashboard_view.xml @@ -0,0 +1,49 @@ + + + + account.journal.dashboard.kanban.inherited + account.journal + + + + + + + + + diff --git a/account_check/views/account_journal_view.xml b/account_check/views/account_journal_view.xml new file mode 100644 index 00000000..2155da05 --- /dev/null +++ b/account_check/views/account_journal_view.xml @@ -0,0 +1,14 @@ + + + + account_check.account.journal.form + account.journal + + + + + + + diff --git a/account_check/views/account_payment_view.xml b/account_check/views/account_payment_view.xml new file mode 100644 index 00000000..a75a8b75 --- /dev/null +++ b/account_check/views/account_payment_view.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + account.payment.form.inherited + account.payment + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {'readonly': ['|',('state','!=','draft'),('payment_method_code', '=', 'delivered_third_check')]} + + + {'readonly': ['|',('state','!=','draft'),('payment_method_code', '=', 'delivered_third_check')]} + + + + + + +
+ + + + account.payment.check.search + account.payment + + + + + + + + + + + +
diff --git a/account_check/views/res_config_settings_view.xml b/account_check/views/res_config_settings_view.xml new file mode 100644 index 00000000..23e9ad01 --- /dev/null +++ b/account_check/views/res_config_settings_view.xml @@ -0,0 +1,27 @@ + + + + account.config.settings.inherit + res.config.settings + + + +

Checks

+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/account_check/views/res_partner_views.xml b/account_check/views/res_partner_views.xml new file mode 100644 index 00000000..976d5823 --- /dev/null +++ b/account_check/views/res_partner_views.xml @@ -0,0 +1,30 @@ + + + + + Check List + ir.actions.act_window + account.check + tree,form + [('partner_id','=',active_id)] + {'search_default_state':'holding'} + current + + + + res.partner.form.inherit.local + res.partner + + + + + + + + + + + diff --git a/account_check/wizard/__init__.py b/account_check/wizard/__init__.py new file mode 100644 index 00000000..e4159c68 --- /dev/null +++ b/account_check/wizard/__init__.py @@ -0,0 +1,6 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from . import account_check_action_wizard +from . import print_pre_numbered_checks diff --git a/account_check/wizard/account_check_action_wizard.py b/account_check/wizard/account_check_action_wizard.py new file mode 100644 index 00000000..72d151b2 --- /dev/null +++ b/account_check/wizard/account_check_action_wizard.py @@ -0,0 +1,56 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class AccountCheckActionWizard(models.TransientModel): + _name = 'account.check.action.wizard' + _description = 'Account Check Action Wizard' + + date = fields.Date( + default=fields.Date.context_today, + required=True, + ) + action_type = fields.Char( + 'Action type passed on the context', + required=True, + ) + journal_id = fields.Many2one( + 'account.journal', string='Journal' + ) + debit_account_id = fields.Many2one( + 'account.account', string='Debit Account' + ) + credit_account_id = fields.Many2one( + 'account.account', string='Credit Account' + ) + + @api.onchange('journal_id') + def onchange_journal_id(self): + if self.journal_id: + self.debit_account_id = self.journal_id.default_debit_account_id + self.credit_account_id = self.journal_id.default_credit_account_id + + @api.multi + def action_confirm(self): + self.ensure_one() + if self.action_type not in [ + 'claim', 'bank_debit', 'reject', 'customer_return']: + raise ValidationError(_( + 'Action %s not supported on checks') % self.action_type) + checks = self.env['account.check'].browse( + self._context.get('active_ids')) + for check in checks: + res = getattr( + check.with_context(action_date=self.date, + journal_id=self.journal_id, + debit_account_id=self.debit_account_id, + credit_account_id=self.credit_account_id + ), self.action_type)() + if len(checks) == 1: + return res + else: + return True diff --git a/account_check/wizard/account_check_action_wizard_view.xml b/account_check/wizard/account_check_action_wizard_view.xml new file mode 100644 index 00000000..cfa879b2 --- /dev/null +++ b/account_check/wizard/account_check_action_wizard_view.xml @@ -0,0 +1,34 @@ + + + + + account.check.action.wizard.form + account.check.action.wizard + +
+ + + + + + + + +
+
+ +
+
+ + + Check Action + account.check.action.wizard + form + form + new + + +
diff --git a/account_check/wizard/print_pre_numbered_checks.py b/account_check/wizard/print_pre_numbered_checks.py new file mode 100644 index 00000000..2ff4daea --- /dev/null +++ b/account_check/wizard/print_pre_numbered_checks.py @@ -0,0 +1,20 @@ + +from odoo import api, fields, models + + +class PrintPreNumberedChecks(models.TransientModel): + _name = 'print.prenumbered.checks' + _description = 'Print Pre-numbered Checks' + + next_check_number = fields.Integer('Next Check Number', required=True) + + @api.multi + def print_checks(self): + check_number = self.next_check_number + payments = self.env['account.payment'].browse( + self.env.context['payment_ids']) + for payment in payments: + payment.check_number = check_number + check_number += 1 + payment.change_check_number() + return payments.do_print_checks() diff --git a/account_check/wizard/print_pre_numbered_checks_view.xml b/account_check/wizard/print_pre_numbered_checks_view.xml new file mode 100644 index 00000000..176417ec --- /dev/null +++ b/account_check/wizard/print_pre_numbered_checks_view.xml @@ -0,0 +1,23 @@ + + + + + Print Pre-numbered Checks + print.prenumbered.checks + +
+

Please enter the number of the first pre-printed check that you are about to print on.

+

This will allow to save on payments the number of the corresponding check.

+ + + +
+
+
+
+
+ +
diff --git a/account_payment_fix/README.rst b/account_payment_fix/README.rst new file mode 100644 index 00000000..e7b40d62 --- /dev/null +++ b/account_payment_fix/README.rst @@ -0,0 +1,69 @@ +.. |company| replace:: ADHOC SA + +.. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png + :alt: ADHOC SA + :target: https://www.adhoc.com.ar + +.. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png + +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +====================================== +Account Payment Fixes and Improvements +====================================== + +Several modification, fixes or improvements to payments: + +* Fix domains for payment method, journal and partner on payment view so that is not loosed when you enter an already created payment. +* It also fix available payment methods when you change several times the journal +* It also restrict destination journal selection if available inbound methods +* We also recreate the menu "Bank and Cash" +* Allow to make payments of child companies + +Installation +============ + +To install this module, you need to: + +#. Just install it + +Configuration +============= + +To configure this module, you need to: + +#. No configuration required + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: http://runbot.adhoc.com.ar/ + +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. + +Credits +======= + +Images +------ + +* |company| |icon| + +Contributors +------------ + +Maintainer +---------- + +|company_logo| + +This module is maintained by the |company|. + +To contribute to this module, please visit https://www.adhoc.com.ar. diff --git a/account_payment_fix/__init__.py b/account_payment_fix/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/account_payment_fix/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_payment_fix/__manifest__.py b/account_payment_fix/__manifest__.py new file mode 100644 index 00000000..851d78b8 --- /dev/null +++ b/account_payment_fix/__manifest__.py @@ -0,0 +1,16 @@ +{ + 'website': 'https://www.codequarters.com', + 'license': 'AGPL-3', + 'category': 'Accounting & Finance', + 'data': [ + 'views/account_payment_view.xml', + ], + 'demo': [], + 'depends': [ + 'account', + ], + 'installable': True, + 'name': 'Account Payment Fix', + 'test': [], + 'version': '12.0.1.1.0', +} diff --git a/account_payment_fix/models/__init__.py b/account_payment_fix/models/__init__.py new file mode 100644 index 00000000..9cc484bd --- /dev/null +++ b/account_payment_fix/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import account_payment +from . import account_invoice diff --git a/account_payment_fix/models/account_invoice.py b/account_payment_fix/models/account_invoice.py new file mode 100644 index 00000000..9838591c --- /dev/null +++ b/account_payment_fix/models/account_invoice.py @@ -0,0 +1,24 @@ +# © 2016 ADHOC SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, api + + +class AccountInvoice(models.Model): + _inherit = "account.invoice" + + @api.multi + def register_payment( + self, payment_line, + writeoff_acc_id=False, writeoff_journal_id=False): + """ + Con esto arreglamos que los pagos puedan pagar contra una cuenta + no conciliable, arreglamos porque odoo manda a conciliar por mas + que no haya facturas y da error, entonces si no hay facturas + que no intente conciliar nada (lo usamos en sipreco esto por ej) + """ + if not self: + return True + return super(AccountInvoice, self).register_payment( + payment_line, writeoff_acc_id=writeoff_acc_id, + writeoff_journal_id=writeoff_journal_id) diff --git a/account_payment_fix/models/account_payment.py b/account_payment_fix/models/account_payment.py new file mode 100644 index 00000000..0f8ce2a7 --- /dev/null +++ b/account_payment_fix/models/account_payment.py @@ -0,0 +1,207 @@ +from odoo import fields, models, api +# from odoo.exceptions import ValidationError +import logging +_logger = logging.getLogger(__name__) + + +class AccountPayment(models.Model): + _inherit = "account.payment" + + name = fields.Char(readonly=False) + state = fields.Selection(track_visibility='always') + amount = fields.Monetary(track_visibility='always') + partner_id = fields.Many2one(track_visibility='always') + journal_id = fields.Many2one(track_visibility='always') + destination_journal_id = fields.Many2one(track_visibility='always') + currency_id = fields.Many2one(track_visibility='always') + # campo a ser extendido y mostrar un nombre detemrinado en las lineas de + # pago de un payment group o donde se desee (por ej. con cheque, retención, + # etc) + payment_method_description = fields.Char( + compute='_compute_payment_method_description', + string='Payment Method', + ) + + @api.multi + def _compute_payment_method_description(self): + for rec in self: + rec.payment_method_description = rec.payment_method_id.display_name + + # nuevo campo funcion para definir dominio de los metodos + payment_method_ids = fields.Many2many( + 'account.payment.method', + compute='_compute_payment_methods', + string='Available payment methods', + ) + journal_ids = fields.Many2many( + 'account.journal', + compute='_compute_journals' + ) + # journal_at_least_type = fields.Char( + # compute='_compute_journal_at_least_type' + # ) + destination_journal_ids = fields.Many2many( + 'account.journal', + compute='_compute_destination_journals' + ) + + @api.multi + @api.depends( + # 'payment_type', + 'journal_id', + ) + def _compute_destination_journals(self): + for rec in self: + domain = [ + ('type', 'in', ('bank', 'cash')), + # al final pensamos mejor no agregar esta restricción, por ej, + # para poder transferir a tarjeta a pagar. Esto solo se usa + # en transferencias + # ('at_least_one_inbound', '=', True), + ('company_id', '=', rec.journal_id.company_id.id), + ('id', '!=', rec.journal_id.id), + ] + rec.destination_journal_ids = rec.journal_ids.search(domain) + + # @api.multi + # @api.depends( + # 'payment_type', + # ) + # def _compute_journal_at_least_type(self): + # for rec in self: + # if rec.payment_type == 'inbound': + # journal_at_least_type = 'at_least_one_inbound' + # else: + # journal_at_least_type = 'at_least_one_outbound' + # rec.journal_at_least_type = journal_at_least_type + + @api.multi + def get_journals_domain(self): + """ + We get domain here so it can be inherited + """ + self.ensure_one() + domain = [('type', 'in', ('bank', 'cash'))] + if self.payment_type == 'inbound': + domain.append(('at_least_one_inbound', '=', True)) + # Al final dejamos que para transferencias se pueda elegir + # cualquier sin importar si tiene outbound o no + # else: + elif self.payment_type == 'outbound': + domain.append(('at_least_one_outbound', '=', True)) + return domain + + @api.multi + @api.depends( + 'payment_type', + ) + def _compute_journals(self): + for rec in self: + rec.journal_ids = rec.journal_ids.search(rec.get_journals_domain()) + + @api.multi + @api.depends( + 'journal_id.outbound_payment_method_ids', + 'journal_id.inbound_payment_method_ids', + 'payment_type', + ) + def _compute_payment_methods(self): + for rec in self: + if rec.payment_type in ('outbound', 'transfer'): + methods = rec.journal_id.outbound_payment_method_ids + else: + methods = rec.journal_id.inbound_payment_method_ids + rec.payment_method_ids = methods + + @api.onchange('payment_type') + def _onchange_payment_type(self): + """ + Sobre escribimos y desactivamos la parte del dominio de la funcion + original ya que se pierde si se vuelve a entrar + """ + if not self.invoice_ids: + # Set default partner type for the payment type + if self.payment_type == 'inbound': + self.partner_type = 'customer' + elif self.payment_type == 'outbound': + self.partner_type = 'supplier' + else: + self.partner_type = False + # limpiamos journal ya que podria no estar disponible para la nueva + # operacion y ademas para que se limpien los payment methods + self.journal_id = False + # # Set payment method domain + # res = self._onchange_journal() + # if not res.get('domain', {}): + # res['domain'] = {} + # res['domain']['journal_id'] = self.payment_type == 'inbound' and [ + # ('at_least_one_inbound', '=', True)] or [ + # ('at_least_one_outbound', '=', True)] + # res['domain']['journal_id'].append(('type', 'in', ('bank', 'cash'))) + # return res + + # @api.onchange('partner_type') + def _onchange_partner_type(self): + """ + Agregasmos dominio en vista ya que se pierde si se vuelve a entrar + Anulamos funcion original porque no haria falta + """ + return True + + @api.onchange('journal_id') + def _onchange_journal(self): + """ + Sobre escribimos y desactivamos la parte del dominio de la funcion + original ya que se pierde si se vuelve a entrar + """ + if self.journal_id: + self.currency_id = ( + self.journal_id.currency_id or self.company_id.currency_id) + # Set default payment method + # (we consider the first to be the default one) + payment_methods = ( + self.payment_type == 'inbound' and + self.journal_id.inbound_payment_method_ids or + self.journal_id.outbound_payment_method_ids) + # si es una transferencia y no hay payment method de origen, + # forzamos manual + if not payment_methods and self.payment_type == 'transfer': + payment_methods = self.env.ref( + 'account.account_payment_method_manual_out') + self.payment_method_id = ( + payment_methods and payment_methods[0] or False) + # si se eligió de origen el mismo diario de destino, lo resetiamos + if self.journal_id == self.destination_journal_id: + self.destination_journal_id = False + # # Set payment method domain + # # (restrict to methods enabled for the journal and to selected + # # payment type) + # payment_type = self.payment_type in ( + # 'outbound', 'transfer') and 'outbound' or 'inbound' + # return { + # 'domain': { + # 'payment_method_id': [ + # ('payment_type', '=', payment_type), + # ('id', 'in', payment_methods.ids)]}} + # return {} + + @api.multi + @api.depends('invoice_ids', 'payment_type', 'partner_type', 'partner_id') + def _compute_destination_account_id(self): + """ + We send force_company on context so payments can be created from parent + companies. We try to send force_company on self but it doesnt works, it + only works sending it on partner + """ + res = super(AccountPayment, self)._compute_destination_account_id() + for rec in self.filtered( + lambda x: not x.invoice_ids and x.payment_type != 'transfer'): + partner = self.partner_id.with_context( + force_company=self.company_id.id) + if self.partner_type == 'customer': + self.destination_account_id = ( + partner.property_account_receivable_id.id) + else: + self.destination_account_id = ( + partner.property_account_payable_id.id) + return res diff --git a/account_payment_fix/views/account_payment_view.xml b/account_payment_fix/views/account_payment_view.xml new file mode 100644 index 00000000..880ff403 --- /dev/null +++ b/account_payment_fix/views/account_payment_view.xml @@ -0,0 +1,51 @@ + + + + + account.payment.form + account.payment + + +
+ + + + + + + [('id', 'in', journal_ids)] + {'no_create': True} + + + [('id', 'in', destination_journal_ids)] + {'no_create': True} + + + [('id', 'in', payment_method_ids)] + {'no_create': True} + + + + + + +
+ + + + + + payment.tree.inherit + account.payment + + + + 1 + + + + + + + +