trytonpsk-account_col/invoice.py

868 lines
34 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import Table
from decimal import Decimal
from datetime import date, timedelta
from trytond.i18n import gettext
from trytond.model import ModelView, ModelSQL, fields, dualmethod
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval, If, Bool
from trytond.wizard import (
Wizard, StateView, Button, StateReport, StateTransition
)
from trytond.transaction import Transaction
from trytond.modules.company import CompanyReport
from trytond.exceptions import UserError
from .exceptions import (
NotificationAuthError, NotificationAuthWarning, InvalidTypeInvoiceError,
NotificationAdvanceWarning,
)
conversor = None
try:
from numword import numword_es
conversor = numword_es.NumWordES()
except:
print("Warning: Does not possible import numword module, please install it...!")
_ZERO = Decimal('0.00')
# Wilson necesitamos que la actualizacion por electronic_invoice de este modulo
# no rompa la retrocompatibilidad, esta variable debe seguir disponible hasta
# que no se migren todos los usuarios
TYPE_INVOICE = [
('', ''),
('C', 'Computador'),
('P', 'POS'),
('M', 'Manual'),
('1', 'Venta Electronica'),
('2', 'Exportacion'),
('3', 'Factura por Contingencia Facturador'),
('4', 'Factura por Contingencia DIAN'),
('91', 'Nota Crédito Eléctronica'),
('92', 'Nota Débito Eléctronica'),
('05', 'Documento soporte en adquisiciones'),
('95', 'Nota de ajuste al documento soporte'),
]
TYPE_INVOICE_OUT = [
('', ''),
('C', 'Computador'),
('P', 'POS'),
('M', 'Manual'),
('1', 'Venta Electronica'),
('2', 'Exportacion'),
('3', 'Factura por Contingencia Facturador'),
('4', 'Factura por Contingencia DIAN'),
('91', 'Nota Crédito Eléctronica'),
('92', 'Nota Débito Eléctronica'),
]
TYPE_INVOICE_IN = [
('', ''),
('05', 'Documento soporte en adquisiciones'),
('95', 'Nota de ajuste al documento soporte'),
]
class Invoice(metaclass=PoolMeta):
__name__ = 'account.invoice'
estimate_pay_date = fields.Function(fields.Date('Estimate Pay Date'),
'get_estimate_pay_date'
)
equivalent_invoice = fields.Boolean('Equivalent Invoice', states={
'invisible': Eval('type') != 'in',
})
resolution = fields.Char('Resolution', states={
'readonly': Eval('state') != 'draft',
})
payment_method = fields.Text('Payment Method',
states={'readonly': Eval('state') != 'draft'},
depends=['payment_term'],
)
number_alternate = fields.Char('Number Alternate',
states={
'readonly': Eval('state') != 'draft',
'invisible': Eval('type') != 'in',
})
delay_amount = fields.Numeric('Delay Amount',
states={'readonly': Eval('state') != 'draft'})
total_amount_words = fields.Function(fields.Char('Total Amount Words'),
'get_total_amount_words')
authorization = fields.Many2One('account.invoice.authorization',
'Authorization', domain=[
('state', '=', 'active'),
('company', '=', Eval('company')),
], states={'invisible': False}
)
invoice_type = fields.Selection(TYPE_INVOICE, 'Type Invoice',
states={
# 'invisible': And(Eval('type') != 'out', ~Eval('equivalent_invoice')),
'readonly': Eval('state').in_(['validated', 'posted', 'paid'])
})
invoice_type_string = invoice_type.translated('invoice_type')
untaxed_amount_cache = fields.Numeric('Untaxed Cache',
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits'])
petty_cash = fields.Boolean('Petty Cash', states={
'invisible': Eval('type') != 'out',
})
@classmethod
def __setup__(cls):
super(Invoice, cls).__setup__()
if 'untaxed_amount_cache' not in cls._check_modify_exclude:
cls._check_modify_exclude.add('untaxed_amount_cache')
if 'number' not in cls._check_modify_exclude:
cls._check_modify_exclude.add('number')
cls.state_string = super(Invoice, cls).state.translated('state')
cls.payment_term.states['required'] = True
cls.payment_term.states['readonly'] = Eval('state') == 'posted'
cls._buttons.update({
'validate_taxes_wizard': {}
})
def print_invoice(self):
# Ignore cache for invoice because high demand of resources
return
@classmethod
def copy(cls, invoices, default=None):
if default is None:
default = {}
default = default.copy()
default['number_alternate'] = None
default['estimate_pay_date'] = None
default['delay_amount'] = None
default['authorization'] = None
return super(Invoice, cls).copy(invoices, default=default)
def get_estimate_pay_date(self, name=None):
if not self.move:
return
for l in self.move.lines:
if l.account.id == self.account.id and l.reconciliation is None:
return l.maturity_date
def get_total_amount_words(self, name=None):
if conversor and self.total_amount:
# if self.second_currency.code == 'USD':
# value = str(self.total_amount).split('.')
# num = (conversor.currency(
# val=tuple(int(n) for n in value),
# old=True,
# hightxt='dolar/s',
# lowtxt='centavo/s',
# jointxt='con')).upper()
# else:
digits = self.company.currency.digits
total_amount = (round(self.total_amount, digits))
num = (conversor.cardinal(int(total_amount))).upper()
return num
@fields.depends('payment_method', 'payment_term')
def on_change_payment_term(self, name=None):
if self.payment_term:
self.payment_method = self.payment_term.description
@fields.depends('invoice_type', 'authorization', 'type')
def on_change_invoice_type(self, name=None):
Authorization = Pool().get('account.invoice.authorization')
if self.invoice_type:
authorizations = Authorization.search([
('kind', '=', self.invoice_type),
('state', '=', 'active'),
])
if len(authorizations) == 1 and authorizations[0].type == self.type:
authorization = authorizations[0]
self.authorization = authorization.id
self.resolution = self.authorization.number
else:
self.authorization = None
self.resolution = None
@fields.depends('equivalent_invoice', 'authorization', 'invoice_type')
def on_change_equivalent_invoice(self, name=None):
if self.equivalent_invoice:
if not self.invoice_type:
self.invoice_type = '05'
self.on_change_invoice_type()
@classmethod
@ModelView.button
def validate_invoice(cls, invoices):
for inv in invoices:
if inv.type == 'out':
if inv.invoice_type in ['', None] or (inv.total_amount < 0 and inv.invoice_type == '1'):
raise InvalidTypeInvoiceError(
gettext('account_col.msg_invalid_type_invoice')
)
else:
cls.validate_tax(inv)
cls.set_number([inv])
super(Invoice, cls).validate_invoice(invoices)
@classmethod
def validate_tax(cls, invoice):
pool = Pool()
Config = pool.get('account.configuration')
Line = pool.get('account.invoice.line')
InvoiceTax = Pool().get('account.invoice.tax')
config = Config(1)
taxes_validate = [t for t in invoice.taxes if t.base and t.tax.base and t.tax.base > t.base]
if taxes_validate and config.remove_tax:
lines_to_change = [l for l in invoice.lines if l.type == 'line']
Line.write(lines_to_change, {'taxes': [
('remove', [t.tax.id for t in taxes_validate])]})
InvoiceTax.delete(taxes_validate)
invoice.save()
@classmethod
@ModelView.button
def post(cls, invoices):
super(Invoice, cls).post(invoices)
for inv in invoices:
inv.write([inv], {
'untaxed_amount_cache': inv.untaxed_amount
})
if inv.type == 'out':
if inv.invoice_type in ['', None]:
raise InvalidTypeInvoiceError(
gettext('account_col.msg_invalid_type_invoice')
)
elif inv.invoice_type == '91' and inv.original_invoice \
and inv.original_invoice.state == 'posted':
writeoff = None
pool = Pool()
config = pool.get('account.configuration')(1)
if inv.currency != inv.company.currency:
writeoff = config.difference_in_exchange
cls.reconcile_invoice(inv, writeoff=writeoff)
if inv.type == 'in':
cls.check_duplicated_reference(inv)
cls.check_advance(inv)
@classmethod
def reconcile_invoice(cls, invoice, writeoff=None):
pool = Pool()
MoveLine = pool.get('account.move.line')
origin = invoice.original_invoice
if origin.amount_to_pay + invoice.amount_to_pay == 0:
to_reconcile_lines = []
for inv in (invoice, origin):
for ml in inv.move.lines:
if inv.account == ml.account:
to_reconcile_lines.append(ml)
to_reconcile_lines.extend([l for l in origin.payment_lines])
MoveLine.reconcile(to_reconcile_lines, writeoff=writeoff)
@classmethod
def set_number(cls, invoices):
for invoice in invoices:
if not invoice.number and invoice.type == 'out' \
and invoice.authorization:
invoice.number = invoice.authorization.sequence.get()
if invoice.type == 'in' and invoice.equivalent_invoice \
and not invoice.number_alternate:
invoice.check_authorization()
invoice.number_alternate = invoice.authorization.sequence.get()
invoice.save()
super(Invoice, cls).set_number(invoices)
def check_authorization(self):
today_ = Pool().get('ir.date').today()
Warning = Pool().get('res.user.warning')
if self.authorization:
next_number = self.authorization.sequence.number_next_internal
auth = self.authorization
res_days = (auth.end_date_auth - today_).days
msg_error = None
msg_warn = None
if res_days <= 0:
msg_error = 'Error, la autorización está vencida'
elif next_number > self.authorization.to_auth:
msg_error = 'Error, la operación supera el rango de numeración de la autorización'
elif res_days <= 10:
msg_warn = 'La autorización esta próxima a vencer, quedan "%s" días' % str(res_days)
elif (int(self.authorization.to_auth) - int(next_number)) <= 10:
msg_warn = 'Los rangos de numeración de la autorización estan proximos a vencer'
warning_key = 'notification_auth_%d' % auth.id
if msg_error:
raise NotificationAuthError(gettext(
'account_col.msg_notification_auth', msg=msg_error)
)
elif msg_warn and Warning.check(warning_key):
raise NotificationAuthWarning(warning_key,
gettext('account_col.msg_notification_auth', msg=msg_warn)
)
def check_advance(self):
Line = Pool().get('account.move.line')
lines = Line.search_count([
('account.type.receivable', '=', True),
('account.reconcile', '=', True),
('party', '=', self.party.id),
('reconciliation', '=', None),
('debit', '>', 0),
])
Warning = Pool().get('res.user.warning')
warning_key = 'notification_advance_%s' % (str(self.party.id) + self.invoice_date.strftime("%y-%m-%d"))
if lines and Warning.check(warning_key):
raise NotificationAdvanceWarning(warning_key,
gettext('account_col.msg_notification_advance', party=self.party.name, msg=lines)
)
def get_move(self):
move = super(Invoice, self).get_move()
if self.description:
move.description = self.description.replace('\n', ' ')
if self.reference:
for line in move.lines:
line.reference = self.reference
# line.description = description.replace('\n', ' ')
return move
@dualmethod
def update_taxes(cls, invoices, exception=False):
super(Invoice, cls).update_taxes(invoices, exception=False)
InvoiceTax = Pool().get('account.invoice.tax')
Tax = Pool().get('account.tax')
taxes_update = Tax.search([
('type', '=', 'fixed'),
('amount', '=', 0),
])
taxes_update_ids = [t.id for t in taxes_update]
for invoice in invoices:
value = 0
for ln in invoice.lines:
if not ln.product or ln.unit_price == 0 or not ln.product.extra_tax:
continue
value += float(ln.product.extra_tax) * ln.quantity
extra_tax_amount = Decimal(str(round(value, 4)))
if invoice.state == 'draft' and extra_tax_amount != 0:
for line_tax in invoice.taxes:
if line_tax.tax.id in taxes_update_ids:
InvoiceTax.write([line_tax], {
'amount': extra_tax_amount
})
invoice.save()
def get_lines_to_pay_cache(self):
Line = Pool().get('account.move.line')
if self.lines_to_pay:
return self.lines_to_pay
Date = Pool().get('ir.date')
move_lines = []
for line in self.lines:
move_lines += line.get_move_lines()
for tax in self.taxes:
move_lines += tax.get_move_lines()
total = Decimal('0.0')
for line in move_lines:
total += line.debit - line.credit
term_lines = [(Date.today(), total)]
if self.payment_term:
term_lines = self.payment_term.compute(
total, self.company.currency, self.invoice_date)
lines_to_pay = []
seq = 0
for date_, amount in term_lines:
seq += 1
line = Line(
id=seq,
maturity_date=date_,
amount_second_currency=0,
debit=abs(amount),
credit=0,
)
lines_to_pay.append(line)
return lines_to_pay
@classmethod
def check_duplicated_reference(cls, invoice):
today = date.today()
target_date = today - timedelta(days=90)
if invoice.reference:
duplicates = cls.search_read([
('reference', '=', invoice.reference),
('party', '=', invoice.party.id),
('invoice_date', '>=', target_date),
], fields_names=['reference'])
if len(duplicates) >= 2:
raise UserError(gettext(
'account_col.msg_duplicated_reference_invoice')
)
class InvoiceLine(ModelSQL, ModelView):
__name__ = 'account.invoice.line'
@classmethod
def __setup__(cls):
super(InvoiceLine, cls).__setup__()
# Set account domain dynamically for kind
cls.account.domain = [
('closed', '!=', True),
('company', '=', Eval('company', -1)),
If(Bool(Eval('_parent_invoice')),
If(Eval('_parent_invoice', {}).get('type') == 'out',
cls._account_domain_('out'),
If(Eval('_parent_invoice', {}).get('type') == 'in',
cls._account_domain_('in'),
['OR',
cls._account_domain_('out'),
cls._account_domain_('in')])),
If(Eval('invoice_type') == 'out',
cls._account_domain_('out'),
If(Eval('invoice_type') == 'in',
cls._account_domain_('in'),
['OR',
cls._account_domain_('out'),
cls._account_domain_('in')]))),
]
@staticmethod
def _account_domain_(type_):
if type_ == 'out':
return ['OR',
('type.revenue', '=', True),
('type.payable', '=', True)
]
elif type_ == 'in':
return ['OR',
('type.expense', '=', True),
('type.debt', '=', True),
('type.stock', '=', True),
]
@classmethod
def trigger_create(cls, records):
for line in records:
if line.type != 'line':
continue
if line.product and line.product.account_category and line.quantity < 0:
category = line.product.account_category
account_id = None
if line.invoice.type == 'in':
if category.account_return_purchase:
account_id = category.account_return_purchase.id
else:
if category.account_return_sale:
account_id = category.account_return_sale.id
if not account_id:
continue
line.write([line], {'account': account_id})
@fields.depends('description')
def on_change_product(self):
super(InvoiceLine, self).on_change_product()
if self.product:
self.description = self.product.name
else:
self.description = None
def get_move_lines(self):
lines = super(InvoiceLine, self).get_move_lines()
if self.product:
for line in lines:
if not self.description:
self.description = self.product.name
self.save()
line.description = self.description.replace('\n', ' ')
return lines
def compute_price_w_tax(self, line):
Tax = Pool().get('account.tax')
res = line.unit_price * Decimal(line.quantity)
if line.taxes and line.unit_price:
tax_list = Tax.compute(line.taxes,
line.unit_price or Decimal('0.0'), 1)
res = sum([t['amount'] for t in tax_list], Decimal('0.0'))
res = (res + line.unit_price) * Decimal(line.quantity)
if line.product.extra_tax:
res += line.product.extra_tax * Decimal(line.quantity)
res = res.quantize(
Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
return res
class InvoiceForceDraft(Wizard):
'Invoice Force Draft'
__name__ = 'account.invoice.force_draft'
start_state = 'force_draft'
force_draft = StateTransition()
def transition_force_draft(self):
pool = Pool()
Invoice = pool.get('account.invoice')
MoveLine = pool.get('account.move.line')
account_invoice = Table('account_invoice')
account_move = Table('account_move')
id_ = Transaction().context['active_id']
invoice = Invoice(id_)
if invoice.electronic_state == 'authorized':
return 'end'
if invoice.move:
move = invoice.move
MoveLine.check_journal_period_modify(move.period, move.journal)
cursor = Transaction().connection.cursor()
if id_:
cursor.execute(*account_invoice.update(
columns=[
account_invoice.state,
account_invoice.invoice_report_cache,
account_invoice.accounting_date,
],
values=["validated", None, None],
where=account_invoice.id == id_)
)
if invoice.move:
cursor.execute(*account_move.update(
columns=[account_move.state],
values=["draft"],
where=account_move.id == invoice.move.id)
)
return 'end'
class EquivalentInvoice(CompanyReport):
'Equivalent Invoice'
__name__ = 'account.invoice.equivalent'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
report_context['company'] = Transaction().context.get('company')
return report_context
class MovesInvoicesStart(ModelView):
'Moves Invoices Start'
__name__ = 'account_invoice.moves.start'
company = fields.Many2One('company.company', 'Company', required=True)
start_date = fields.Date('Start Date')
end_date = fields.Date('End Date', required=True)
type_inv = fields.Selection([
('out', 'Invoice'),
('out_credit_note', 'Out Credit Note'),
('in', 'Supplier Invoice'),
('in_credit_note', 'In Credit Note'),
], 'Type', required=True)
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_end_date():
Date = Pool().get('ir.date')
return Date.today()
class MovesInvoices(Wizard):
'Moves Invoices'
__name__ = 'account_invoice.moves'
start = StateView('account_invoice.moves.start',
'account_col.moves_invoices_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('account_invoice.moves_report')
def do_print_(self, action):
data = {
'company': self.start.company.id,
'start_date': self.start.start_date,
'end_date': self.start.end_date,
'type_invoice': self.start.type_inv,
}
return action, data
def transition_print_(self):
return 'end'
class MovesInvoicesReport(CompanyReport):
'Moves Invoices Report'
__name__ = 'account_invoice.moves_report'
@classmethod
def get_domain_invoice(cls, data):
if data['type_invoice'] == "out_credit_note":
type_invoice = 'out'
elif data['type_invoice'] == "in_credit_note":
type_invoice = 'in'
else:
type_invoice = data['type_invoice']
dom_invoices = [
('company', '=', data['company']),
('type', '=', type_invoice),
('state', 'in', ['validated', 'posted', 'paid']),
('move', '!=', None),
]
return dom_invoices
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Company = pool.get('company.company')
Invoice = pool.get('account.invoice')
company = Company(data['company'])
start = data['start_date']
end = data['end_date']
dom_invoices = cls.get_domain_invoice(data)
if end != None:
dom_invoices.append(
('invoice_date', '<=', end),
)
if start != None:
dom_invoices.append(
('invoice_date', '>=', start),
)
invoices = Invoice.search(dom_invoices,
order=[('invoice_date', 'ASC')])
moves_filtered = []
for invoice in invoices:
if invoice.untaxed_amount < _ZERO:
if "credit" in data['type_invoice']:
moves_filtered.append(invoice.move)
else:
continue
else:
if "credit" not in data['type_invoice']:
moves_filtered.append(invoice.move)
else:
continue
report_context['records'] = moves_filtered
report_context['moves_filtered'] = moves_filtered
report_context['company'] = company
report_context['start'] = start
report_context['end'] = end
return report_context
class InvoiceUpdateStart(ModelView):
'Invoice Update Start'
__name__ = 'account_invoice.update.start'
date = fields.Date('Date')
description = fields.Char('Description')
tax_add = fields.Many2One('account.tax', 'Add Tax', domain=[
('group.kind', '=', Eval('group_tax'))
], depends=['group_tax'])
tax_remove = fields.Many2One('account.tax', 'Remove Tax', domain=[
('group.kind', '=', Eval('group_tax'))
], depends=['group_tax'])
group_tax = fields.Char('Group Tax')
number_alternate = fields.Char('New Number Inv. Equi')
number = fields.Char('New Number')
party = fields.Many2One('party.party', 'Party')
@staticmethod
def default_group_tax():
Invoice = Pool().get('account.invoice')
invoice_ids = Transaction().context['active_ids']
invoice = Invoice(invoice_ids[0])
if invoice.type == 'in':
return 'purchase'
else:
return 'sale'
class InvoiceUpdate(Wizard):
'Invoice Update'
__name__ = 'account_invoice.update'
start = StateView('account_invoice.update.start',
'account_col.invoice_update_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'accept', 'tryton-ok', default=True),
])
accept = StateTransition()
def transition_accept(self):
Invoice = Pool().get('account.invoice')
Line = Pool().get('account.invoice.line')
InvoiceTax = Pool().get('account.invoice.tax')
invoice, = Invoice.browse([Transaction().context['active_id']])
values = {}
if self.start.party:
values['party'] = self.start.party.id
values['invoice_address'] = self.start.party.addresses[0].id
if self.start.date:
values['invoice_date'] = self.start.date
if self.start.description:
values['description'] = self.start.description
if self.start.number:
account_invoice = Table('account_invoice')
id_ = Transaction().context['active_id']
cursor = Transaction().connection.cursor()
if id_:
cursor.execute(*account_invoice.update(
columns=[account_invoice.number],
values=[self.start.number],
where=account_invoice.id == id_)
)
if self.start.number_alternate:
account_invoice = Table('account_invoice')
id_ = Transaction().context['active_id']
cursor = Transaction().connection.cursor()
if id_:
cursor.execute(*account_invoice.update(
columns=[account_invoice.number_alternate],
values=[self.start.number],
where=account_invoice.id == id_)
)
if invoice.state == 'draft' and not invoice.cufe and values:
if self.start.party:
for line in invoice.lines:
line.party = self.start.party.id
Line.write(list(invoice.lines), {'party': self.start.party.id})
Invoice.write([invoice], values)
if (self.start.tax_add or self.start.tax_remove) and invoice:
lines_to_change = []
for line in invoice.lines:
if line.type != 'line':
continue
lines_to_change.append(line)
if lines_to_change:
if self.start.tax_add:
Line.write(lines_to_change, {'taxes': [
('add', [self.start.tax_add.id])]})
if self.start.tax_remove:
Line.write(lines_to_change, {'taxes': [
('remove', [self.start.tax_remove.id])]})
if self.start.tax_add:
invoice.on_change_taxes()
if self.start.tax_remove:
for itax in invoice.taxes:
if itax.tax.id == self.start.tax_remove.id:
InvoiceTax.delete([itax])
invoice.save()
return 'end'
class InvoiceReport(metaclass=PoolMeta):
__name__ = 'account.invoice'
@classmethod
def _execute(cls, records, header, data, action):
# Ensure to restore original context
# set_lang may modify it
with Transaction().set_context(Transaction().context):
report_context = cls.get_context(records, header, data)
return cls.convert(action, cls.render(action, report_context))
class InvoiceTax(ModelSQL):
'Invoice Tax'
__name__ = 'account.invoice.tax'
def get_move_lines(self):
'''
Return a list of move lines instances for invoice tax
'''
lines = super(InvoiceTax, self).get_move_lines()
lines_ = []
Module = Pool().get('ir.module')
MoveLine = Pool().get('account.move.line')
Currency = Pool().get('currency.currency')
module, = Module.search(['name', '=', 'analytic_account'])
if module.state == 'activated':
AnalyticLine = Pool().get('analytic_account.line')
op = self.invoice
for ln in lines:
ln.reference = self.invoice.reference
if hasattr(self.invoice, 'operation_center') and self.invoice.operation_center:
ln.operation_center = self.invoice.operation_center
if hasattr(self.invoice, 'analytic_account') and self.invoice.analytic_account:
analytic_ = self.invoice.analytic_account
# Add analytic when IVA mayor valor del costo gasto
if self.tax.iva_costo and self.invoice.type == 'in' and self.tax.classification == 'iva':
grouped = {}
for line in self.invoice.lines:
if self.tax in line.taxes:
op = line.operation_center.id if hasattr(line, 'operation_center') and line.operation_center else None
analytic = line.analytic_accounts[0].account.id if hasattr(line, 'analytic_accounts') and line.analytic_accounts and line.analytic_accounts[0].account else None
key = str(op) + '-' +str(analytic)
try:
grouped[key]['base'] += line.amount
except:
new_line = MoveLine()
for field in [
'debit', 'credit', 'tax_lines', 'reference', 'second_currency',
'amount_second_currency', 'description', 'account', 'origin', 'party']:
setattr(new_line, field, getattr(ln, field))
grouped[key] = {
'key': key,
'line': new_line,
'base': abs(line.amount),
'analytic_account': analytic if analytic else analytic_,
}
if op:
grouped[key]['line'].operation_center = op
for m in grouped.values():
amount_ = m['base']* self.tax.rate * Decimal(self.tax.taxable_base/100) \
if self.tax.taxable_base else m['base']* self.tax.rate
amount_ = amount_.quantize(Decimal(1) / 10 ** 2)
if self.invoice.currency != self.invoice.company.currency:
with Transaction().set_context(date=self.invoice.currency_date):
amount = Currency.compute(self.invoice.currency,
amount_, self.invoice.company.currency)
m['line'].amount_second_currency = amount_
else:
amount = amount_
for t in m['line'].tax_lines:
t.amount = amount
if m['line'].debit == 0:
m['line'].credit = abs(amount)
else:
m['line'].debit = abs(amount)
analytic_account_id = m['analytic_account']
if not analytic_account_id and hasattr(self.invoice, 'analytic_account') and self.invoice.analytic_account:
analytic_account_id = self.invoice.analytic_account.id
if not m['analytic_account']:
raise UserError(gettext('account_col.msg_analytic_required'))
analytic_line = AnalyticLine(
account=m['analytic_account'],
debit=m['line'].debit,
credit=m['line'].credit,
date=self.invoice.invoice_date,
)
m['line'].analytic_lines = [analytic_line]
lines_.append(m['line'])
if lines_:
lines = lines_
return lines
# class WriteOff():
# # Don't remove this class is neccesary for writeoff account move
# pass