# The COPYRIGHT file at the top level of this repository contains the full # copyright notices and license terms. from functools import wraps from decimal import Decimal from trytond.model import fields from trytond.pyson import Eval from trytond.pool import Pool from trytond.transaction import Transaction from trytond.exceptions import UserError from trytond.i18n import gettext try: from \ trytond.modules.analytic_account_root_mandatory_bypass.analytic_account \ import suppress_root_mandatory except ModuleNotFoundError: def suppress_root_mandatory(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper class ApplyMethodCostMixin(object): __slots__ = () invoice_party = fields.Many2One('party.party', 'Invoice Party', states={ 'invisible': ~Eval('apply_method', '').in_( ['invoice_in', 'invoice_out']) }) @classmethod def __setup__(cls): super(ApplyMethodCostMixin, cls).__setup__() cls.apply_method.selection.append(('invoice_out', 'Invoice Out')) cls.apply_method.selection.append(('invoice_in', 'Invoice In')) class ApplyMethodCostDocumentMixin(ApplyMethodCostMixin): __slots__ = () @classmethod def __setup__(cls): super().__setup__() for fieldname in {'formula', 'amount', 'invoice_party'}: _field = getattr(cls, fieldname) if 'readonly' in _field.states: _field.states['readonly'] |= (Eval('invoice_lines', [])) else: _field.states['readonly'] = (Eval('invoice_lines', [])) @fields.depends('invoice_lines') def get_compute_formula(self, name=None): # do not compute amount if invoice lines exist return bool(super().get_compute_formula(name) and not self.invoice_lines) @classmethod def get_blocked_attributes(cls): return super(ApplyMethodCostDocumentMixin, cls).get_blocked_attributes() + ['invoice_party'] @classmethod def _apply_method(cls, apply_method, costs): if apply_method in ['invoice_in', 'invoice_out']: for cost in costs: if cost.amount and not cost.invoice_party: raise UserError(gettext( 'document_cost_apply_invoice.msg_document_cost_apply_invoice_invoice_no_party', type_=cost.type_.rec_name, cost=cost.rec_name if isinstance( cost.document, tuple) else cost.document.rec_name)) cls.create_invoice_lines(apply_method, costs) else: super(ApplyMethodCostDocumentMixin, cls)._apply_method( apply_method, costs) @classmethod def _unapply_method(cls, apply_method, costs): InvoiceLine = Pool().get('account.invoice.line') with Transaction().set_user(0, set_context=True): if apply_method in ['invoice_in', 'invoice_out']: lines = InvoiceLine.search([ ('origin.id', 'in', [c.id for c in costs], cls.__name__)]) for line in lines: if line.invoice: raise UserError(gettext( 'document_cost_apply_invoice.msg_document_cost_apply_invoice_unapply_cost', origin=line.origin.rec_name, invoice=line.invoice.rec_name)) if lines: InvoiceLine.delete(lines) else: super(ApplyMethodCostDocumentMixin, cls)._unapply_method( apply_method, costs) @classmethod @suppress_root_mandatory def create_invoice_lines(cls, apply_method, costs): pool = Pool() InvoiceLine = pool.get('account.invoice.line') lines = [] for cost in costs: _lines = cost._get_invoice_lines(apply_method) if not _lines: continue lines.extend(_lines) with Transaction().set_user(0, set_context=True): InvoiceLine.save(lines) return lines def _get_invoice_lines(self, apply_method): pool = Pool() InvoiceLine = pool.get('account.invoice.line') if not self.amount or self.invoice_lines: return [] line = InvoiceLine() line.type = 'line' line.invoice = None line.invoice_type = apply_method[8:] if isinstance(self.document, tuple): line.company = self.company line.currency = self.currency else: line.company = self.document.company line.currency = self.document.company.currency line.party = self.invoice_party if self.type_.product: line.product = self.type_.product line.unit = self.type_.product.default_uom line.on_change_product() else: line.account = self.type_.account_used line.on_change_account() line.origin = self line.quantity = self.quantity digits = InvoiceLine.unit_price.digits[1] line.unit_price = ((self.amount / (Decimal(str(self.quantity)) if self.quantity else Decimal('1.0')) ) * (-1 if line.invoice_type == 'out' else 1)).quantize( Decimal(10) ** -Decimal(digits)) line.amount = line.on_change_with_amount() return [line] class ApplyInvoiceAccountMixin(object): __slots__ = () @classmethod def _account_domain(cls): InvoiceLine = Pool().get('account.invoice.line') res = super()._account_domain() return [ ('invoice_out', [ InvoiceLine._account_domain('out')]), ('invoice_in', [ InvoiceLine._account_domain('in')]), ] + res