diff --git a/l10n_it_reverse_charge/README.rst b/l10n_it_reverse_charge/README.rst index fe74e5ce71b6..de5b8211e2bf 100644 --- a/l10n_it_reverse_charge/README.rst +++ b/l10n_it_reverse_charge/README.rst @@ -119,7 +119,8 @@ Contributors * Davide Corio * Alex Comba -* Lorenzo Battistini +* Giacomo Grasso Maintainer ---------- diff --git a/l10n_it_reverse_charge/__init__.py b/l10n_it_reverse_charge/__init__.py index 0d63fc7d809c..f0e81ea288d5 100644 --- a/l10n_it_reverse_charge/__init__.py +++ b/l10n_it_reverse_charge/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Davide Corio # Copyright 2017 Alex Comba - Agile Business Group # Copyright 2017 Lorenzo Battistini - Agile Business Group diff --git a/l10n_it_reverse_charge/__manifest__.py b/l10n_it_reverse_charge/__manifest__.py index 3bd0103e6a4c..35383dbcbdfb 100644 --- a/l10n_it_reverse_charge/__manifest__.py +++ b/l10n_it_reverse_charge/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Davide Corio # Copyright 2017 Alex Comba - Agile Business Group # Copyright 2017 Lorenzo Battistini - Agile Business Group @@ -7,14 +6,14 @@ { 'name': 'Reverse Charge IVA', - 'version': '10.0.1.1.1', + 'version': '11.0.1.0.0', 'category': 'Localization/Italy', 'summary': 'Reverse Charge for Italy', 'author': 'Odoo Italia Network,Odoo Community Association (OCA)', 'license': 'LGPL-3', 'website': 'https://www.odoo-italia.net', 'depends': [ - 'account_accountant', + # 'account_menu_group', 'account_cancel', ], 'data': [ @@ -24,6 +23,7 @@ 'views/account_invoice_view.xml', 'views/account_fiscal_position_view.xml', 'views/account_rc_type_view.xml', + 'views/account_report_invoice.xml', ], 'installable': True, } diff --git a/l10n_it_reverse_charge/data/rc_type.xml b/l10n_it_reverse_charge/data/rc_type.xml index d111753edecb..9fba2cfbff96 100644 --- a/l10n_it_reverse_charge/data/rc_type.xml +++ b/l10n_it_reverse_charge/data/rc_type.xml @@ -21,4 +21,4 @@ - \ No newline at end of file + diff --git a/l10n_it_reverse_charge/models/__init__.py b/l10n_it_reverse_charge/models/__init__.py index cddb669c18b2..2e574604a3ba 100644 --- a/l10n_it_reverse_charge/models/__init__.py +++ b/l10n_it_reverse_charge/models/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Davide Corio # Copyright 2017 Alex Comba - Agile Business Group # Copyright 2017 Lorenzo Battistini - Agile Business Group diff --git a/l10n_it_reverse_charge/models/account_fiscal_position.py b/l10n_it_reverse_charge/models/account_fiscal_position.py index e6eeebcdf0c2..0c26e6792c9f 100644 --- a/l10n_it_reverse_charge/models/account_fiscal_position.py +++ b/l10n_it_reverse_charge/models/account_fiscal_position.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2016 Davide Corio # Copyright 2017 Alex Comba - Agile Business Group # Copyright 2017 Lorenzo Battistini - Agile Business Group diff --git a/l10n_it_reverse_charge/models/account_invoice.py b/l10n_it_reverse_charge/models/account_invoice.py index 85919aa94b3d..ed09c6d666dc 100644 --- a/l10n_it_reverse_charge/models/account_invoice.py +++ b/l10n_it_reverse_charge/models/account_invoice.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2016 Davide Corio # Copyright 2017 Alex Comba - Agile Business Group # Copyright 2017 Lorenzo Battistini - Agile Business Group @@ -35,6 +34,27 @@ class AccountInvoice(models.Model): comodel_name='account.invoice', string='RC Self Purchase Invoice', copy=False, readonly=True) + rc_partner_supplier_id = fields.Many2one( + 'res.partner', string='Partner RC', change_default=True, + required=False, readonly=True, + states={'draft': [('readonly', False)]}, + track_visibility='always') + + rc_debit_credit_transfer_id = fields.Many2one( + 'account.move', required=False, readonly=True) + company_partner = fields.Boolean(compute='_compute_company_partner') + + rc_payment_move_id = fields.Many2one( + string="Reconciliation move", comodel_name='account.move', + required=False, readonly=True, + help="Move that reconciles the purchase and sale invoice") + + @api.depends('partner_id') + def _compute_company_partner(self): + for el in self: + if el.partner_id == el.company_id.partner_id: + el.company_partner = True + def rc_inv_line_vals(self, line): return { 'product_id': line.product_id.id, @@ -42,7 +62,7 @@ def rc_inv_line_vals(self, line): 'uom_id': line.product_id.uom_id.id, 'price_unit': line.price_unit, 'quantity': line.quantity, - } + } def rc_inv_vals(self, partner, account, rc_type, lines): if self.type == 'in_invoice': @@ -50,7 +70,7 @@ def rc_inv_vals(self, partner, account, rc_type, lines): else: type = 'out_refund' - return { + vals = { 'partner_id': partner.id, 'type': type, 'account_id': account.id, @@ -61,9 +81,22 @@ def rc_inv_vals(self, partner, account, rc_type, lines): 'origin': self.number, 'rc_purchase_invoice_id': self.id, 'name': rc_type.self_invoice_text, - 'fiscal_position_id': None + 'fiscal_position_id': None, + 'rc_partner_supplier_id': None } + # controllare se rc_type è other e se partner è la company + # setto rc_partner_supplier con il partner di partenza + if (rc_type.partner_type == 'other' and + partner == self.company_id.partner_id): + # CREAZIONE DI UN'AUTOFATTURA + vals['rc_partner_supplier_id'] = ( + self.rc_partner_supplier_id.id or + self.partner_id.id + ) + + return vals + def get_inv_line_to_reconcile(self): for inv_line in self.move_id.line_ids: if (self.type == 'in_invoice') and inv_line.credit: @@ -85,7 +118,7 @@ def rc_payment_vals(self, rc_type): 'journal_id': rc_type.payment_journal_id.id, # 'period_id': self.period_id.id, 'date': self.date, - } + } def rc_credit_line_vals(self, journal): credit = debit = 0.0 @@ -100,7 +133,7 @@ def rc_credit_line_vals(self, journal): 'debit': debit, 'account_id': journal.default_credit_account_id.id, 'company_id': self.company_id.id, - } + } def rc_debit_line_vals(self, amount=None): credit = debit = 0.0 @@ -121,14 +154,14 @@ def rc_debit_line_vals(self, amount=None): 'account_id': self.get_inv_line_to_reconcile().account_id.id, 'partner_id': self.partner_id.id, 'company_id': self.company_id.id - } + } def rc_invoice_payment_vals(self, rc_type): return { 'journal_id': rc_type.payment_journal_id.id, # 'period_id': self.period_id.id, 'date': self.date, - } + } def rc_payment_credit_line_vals(self, invoice): credit = debit = 0.0 @@ -144,7 +177,7 @@ def rc_payment_credit_line_vals(self, invoice): invoice).account_id.id, 'partner_id': invoice.partner_id.id, 'company_id': self.company_id.id - } + } def rc_payment_debit_line_vals(self, invoice, journal): credit = debit = 0.0 @@ -158,7 +191,7 @@ def rc_payment_debit_line_vals(self, invoice, journal): 'credit': credit, 'account_id': journal.default_credit_account_id.id, 'company_id': self.company_id.id - } + } def reconcile_supplier_invoice(self): rc_type = self.fiscal_position_id.rc_type_id @@ -206,24 +239,32 @@ def prepare_reconcile_supplier_invoice(self): rc_type.payment_journal_id) payment_debit_line_data = self.rc_debit_line_vals() - rc_payment.line_ids = [ - (0, 0, payment_debit_line_data), - (0, 0, payment_credit_line_data), - ] + # Avoid payment lines without amounts + if (payment_credit_line_data['debit'] or + payment_credit_line_data['credit']): + rc_payment.line_ids = [ + (0, 0, payment_debit_line_data), + (0, 0, payment_credit_line_data), + ] return rc_payment def partially_reconcile_supplier_invoice(self, rc_payment): move_line_model = self.env['account.move.line'] + inv_line_to_reconcile = self.get_inv_line_to_reconcile() + payment_debit_line = False for move_line in rc_payment.line_ids: + if (inv_line_to_reconcile.account_id.id != + move_line.account_id.id): + continue # testa se nota credito o debito - if (self.type == 'in_invoice') and move_line.debit: + if self.type == 'in_invoice' and move_line.debit: payment_debit_line = move_line - elif (self.type == 'in_refund') and move_line.credit: + elif self.type == 'in_refund' and move_line.credit: payment_debit_line = move_line - inv_lines_to_rec = move_line_model.browse( - [self.get_inv_line_to_reconcile().id, - payment_debit_line.id]) - inv_lines_to_rec.reconcile() + if payment_debit_line: + inv_lines_to_rec = move_line_model.browse( + [inv_line_to_reconcile.id, payment_debit_line.id]) + inv_lines_to_rec.reconcile() def reconcile_rc_invoice(self, rc_payment): rc_type = self.fiscal_position_id.rc_type_id @@ -286,6 +327,7 @@ def generate_self_invoice(self): inv_vals = self.rc_inv_vals( rc_partner, rc_account, rc_type, rc_invoice_lines) + inv_vals['comment'] = self.fiscal_position_id.note # create or write the self invoice if self.rc_self_invoice_id: # this is needed when user takes back to draft supplier @@ -304,6 +346,7 @@ def generate_self_invoice(self): self.reconcile_supplier_invoice() else: rc_payment = self.prepare_reconcile_supplier_invoice() + self.write({'rc_payment_move_id': rc_payment.id}) self.reconcile_rc_invoice(rc_payment) self.partially_reconcile_supplier_invoice(rc_payment) @@ -339,10 +382,20 @@ def generate_supplier_self_invoice(self): supplier_invoice.action_invoice_open() supplier_invoice.fiscal_position_id = self.fiscal_position_id.id + self._set_rc_partner_supplier_id(rc_type, supplier_invoice) + + def _set_rc_partner_supplier_id(self, rc_type, supplier_invoice): + if (rc_type.partner_type == 'other' and + rc_type.partner_id == self.company_id.partner_id): + partner_ref = self.rc_partner_supplier_id or self.partner_id + # ref del partner da cui si stanno generando le autofatture + supplier_invoice.rc_partner_supplier_id = partner_ref + @api.multi def invoice_validate(self): self.ensure_one() res = super(AccountInvoice, self).invoice_validate() + fp = self.fiscal_position_id rc_type = fp and fp.rc_type_id if rc_type and rc_type.method == 'selfinvoice'\ @@ -353,8 +406,84 @@ def invoice_validate(self): # See with_supplier_self_invoice field help self.generate_supplier_self_invoice() self.rc_self_purchase_invoice_id.generate_self_invoice() + + if (rc_type and + rc_type.method == 'selfinvoice' and + self.amount_total and + self.type in ('in_invoice', 'in_refund') and + self.company_partner is True): + + self.transfer_debit_partner_supplier() return res + def transfer_debit_partner_supplier(self): + """ + Se il partner della fattura di partenza (no RC) è la company ed + è una fattura di acquisto, deve essere aperto un debito vs il + parnter di partenza e chiuso quello del partner + """ + # Credit/debit logic refund + ml_company = False + for ml in self.move_id.line_ids: + if ml.account_id.user_type_id.type in ['receivable', 'payable']: + ml_company = ml + break + + if ml_company: + move_lines = [] + # Refund my company + if ml_company.debit: + desc = _('Credit transfer for reverse charge') + else: + desc = _('Debit transfer for reverse charge') + company_line = { + 'partner_id': self.partner_id.id, + 'account_id': ml_company.account_id.id, + 'credit': (ml_company and + ml_company.debit and + self.residual or 0), + 'debit': (ml_company and + ml_company.credit and + self.residual or 0), + 'name': desc, + } + move_lines.append((0, 0, company_line)) + # Add debit/credit to RC partner + partner_rc_line = { + 'partner_id': self.rc_partner_supplier_id.id, + 'account_id': ml_company.account_id.id, + 'credit': (ml_company and + ml_company.credit and + self.residual or 0), + 'debit': (ml_company and + ml_company.debit and + self.residual or 0), + 'name': desc, + } + move_lines.append((0, 0, partner_rc_line)) + + move_val = { + 'date': self.move_id.date, + 'company_id': self.move_id.company_id.id, + 'journal_id': + self.fiscal_position_id.rc_type_id.payment_journal_id.id, + 'line_ids': move_lines + } + move_transfer = self.env['account.move'].create(move_val) + self.write({'rc_debit_credit_transfer_id': move_transfer.id}) + + # Reconcile supplier invoice with transfer + lines_to_reconcile = [] + domain = [('move_id', '=', move_transfer.id), + ('partner_id', '=', self.partner_id.id)] + ml_transfer_company = self.env['account.move.line']\ + .search(domain, order='id', limit=1) + if ml_transfer_company: + lines_to_reconcile = ml_transfer_company + ml_company + lines_to_reconcile.reconcile() + + return move_transfer + def remove_rc_payment(self): inv = self if inv.payment_move_line_ids: diff --git a/l10n_it_reverse_charge/models/account_rc_type.py b/l10n_it_reverse_charge/models/account_rc_type.py index 3b15354c7031..d6d5f38f0678 100644 --- a/l10n_it_reverse_charge/models/account_rc_type.py +++ b/l10n_it_reverse_charge/models/account_rc_type.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2016 Davide Corio # Copyright 2017 Alex Comba - Agile Business Group # Copyright 2017 Lorenzo Battistini - Agile Business Group @@ -80,7 +79,9 @@ class AccountRCType(models.Model): string='Self Invoice Tax Mapping', copy=False) description = fields.Text('Description') - self_invoice_text = fields.Text('Text in Self Invoice') + self_invoice_text = fields.Text( + string='Text in Self Invoice', + help="Reference/Description in Customer Invoice") @api.multi @api.constrains('with_supplier_self_invoice', 'tax_ids') diff --git a/l10n_it_reverse_charge/tests/__init__.py b/l10n_it_reverse_charge/tests/__init__.py index ce1e7e8ec806..0bbf88181787 100644 --- a/l10n_it_reverse_charge/tests/__init__.py +++ b/l10n_it_reverse_charge/tests/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from . import test_rc diff --git a/l10n_it_reverse_charge/tests/test_rc.py b/l10n_it_reverse_charge/tests/test_rc.py index 012bc6094ee0..cab87bd482f8 100644 --- a/l10n_it_reverse_charge/tests/test_rc.py +++ b/l10n_it_reverse_charge/tests/test_rc.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2018 Simone Rubino - Agile Business Group # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo.exceptions import UserError diff --git a/l10n_it_reverse_charge/views/account_invoice_view.xml b/l10n_it_reverse_charge/views/account_invoice_view.xml index 7926d9ca12e4..cd50a5aedb66 100644 --- a/l10n_it_reverse_charge/views/account_invoice_view.xml +++ b/l10n_it_reverse_charge/views/account_invoice_view.xml @@ -11,12 +11,21 @@ account.invoice + + + + + + + @@ -43,6 +52,12 @@ account.invoice + + + + + + + + +