If you want to get right debit / credit in accounting module follow the following code .
target_currency = target_currency or company_currency
line_currency = line.currency_id or company_currency
amount_currency_str = ""
total_amount_currency_str = ""
if line_currency != company_currency:
total_amount = line.amount_currency
actual_debit = debit > 0 and amount_currency or 0.0
actual_credit = credit > 0 and -amount_currency or 0.0
else:
total_amount = abs(debit - credit)
actual_debit = debit > 0 and amount or 0.0
actual_credit = credit > 0 and -amount or 0.0
if line_currency != target_currency:
amount_currency_str = formatLang(self.env, abs(actual_debit or actual_credit), currency_obj=line_currency)
total_amount_currency_str = formatLang(self.env, total_amount, currency_obj=line_currency)
ctx = context.copy()
ctx.update({'date': line.date})
total_amount = line_currency.with_context(ctx).compute(total_amount, target_currency)
actual_debit = line_currency.with_context(ctx).compute(actual_debit, target_currency)
actual_credit = line_currency.with_context(ctx).compute(actual_credit, target_currency)
amount_str = formatLang(self.env, abs(actual_debit or actual_credit), currency_obj=target_currency)
total_amount_str = formatLang(self.env, total_amount, currency_obj=target_currency)
ret_line['debit'] = abs(actual_debit)
ret_line['credit'] = abs(actual_credit)
ret_line['amount_str'] = amount_str
ret_line['total_amount_str'] = total_amount_str
ret_line['amount_currency_str'] = amount_currency_str
ret_line['total_amount_currency_str'] = total_amount_currency_str
ret.append(ret_line)
return ret
@api.v7
def process_reconciliations(self, cr, uid, data, context=None):
""" Used to validate a batch of reconciliations in a single call
:param data: list of dicts containing:
- 'type': either 'partner' or 'account'
- 'id': id of the affected res.partner or account.account
- 'mv_line_ids': ids of exisiting account.move.line to reconcile
- 'new_mv_line_dicts': list of dicts containing values suitable for account_move_line.create()
"""
for datum in data:
if len(datum['mv_line_ids']) >= 1 or len(datum['mv_line_ids']) + len(datum['new_mv_line_dicts']) >= 2:
self.process_reconciliation(cr, uid, datum['mv_line_ids'], datum['new_mv_line_dicts'], context=context)
if datum['type'] == 'partner':
partners = self.pool['res.partner'].browse(cr, uid, datum['id'], context=context)
self.pool['res.partner'].mark_as_reconciled(cr, uid, partners.ids, context=context)
if datum['type'] == 'account':
accounts = self.pool['account.account'].browse(cr, uid, datum['id'], context=context)
self.pool['account.account'].mark_as_reconciled(cr, uid, accounts.ids, context=context)
@api.v7
def process_reconciliation(self, cr, uid, mv_line_ids, new_mv_line_dicts, context=None):
return self.browse(cr, uid, mv_line_ids, context).process_reconciliation(new_mv_line_dicts)
@api.v8
def process_reconciliation(self, new_mv_line_dicts):
""" Create new move lines from new_mv_line_dicts (if not empty) then call reconcile_partial on self and new move lines
:param new_mv_line_dicts: list of dicts containing values suitable fot account_move_line.create()
"""
if len(self) < 1 or len(self) + len(new_mv_line_dicts) < 2:
raise UserError(_('Error!'), _('A reconciliation must involve at least 2 move lines.'))
# Create writeoff move lines
if len(new_mv_line_dicts) > 0:
writeoff_lines = self.env['account.move.line']
company_currency = self[0].account_id.company_id.currency_id
account_currency = self[0].account_id.currency_id or company_currency
for mv_line_dict in new_mv_line_dicts:
if account_currency != company_currency:
mv_line_dict['debit'] = account_currency.compute(mv_line_dict['debit'], company_currency)
mv_line_dict['credit'] = account_currency.compute(mv_line_dict['credit'], company_currency)
writeoff_lines += self._create_writeoff(mv_line_dict)
(self + writeoff_lines).reconcile()
else:
self.reconcile()
####################################################
# Reconciliation methods
####################################################
def _get_pair_to_reconcile(self):
#field is either 'amount_residual' or 'amount_residual_currency' (if the reconciled account has a secondary currency set)
field = self[0].account_id.currency_id and 'amount_residual_currency' or 'amount_residual'
#target the pair of move in self that are the oldest
sorted_moves = sorted(self, key=lambda a: a.date)
debit = credit = False
for aml in sorted_moves:
if credit != False and debit != False:
break
if aml[field] > 0 and debit == False:
debit = aml
elif aml[field] < 0 and credit == False:
credit = aml
return debit, credit
def auto_reconcile_lines(self):
""" This function iterates recursively on the recordset given as parameter as long as it
can find a debit and a credit to reconcile together. It returns the recordset of the
account move lines that were not reconciled during the process.
"""
if not self.ids:
return self
field = self[0].account_id.currency_id and 'amount_residual_currency' or 'amount_residual'
sm_debit_move, sm_credit_move = self._get_pair_to_reconcile()
#there is no more pair to reconcile so return what move_line are left
if not sm_credit_move or not sm_debit_move:
return self
#Reconcile the pair together
amount_reconcile = min(sm_debit_move[field], -sm_credit_move[field])
#Remove from recordset the one(s) that will be totally reconciled
if amount_reconcile == sm_debit_move[field]:
self -= sm_debit_move
if amount_reconcile == -sm_credit_move[field]:
self -= sm_credit_move
#Check for the currency and amount_currency we can set
currency = False
amount_reconcile_currency = 0
if sm_debit_move.currency_id == sm_credit_move.currency_id and sm_debit_move.currency_id.id:
currency = sm_credit_move.currency_id.id
amount_reconcile_currency = min(sm_debit_move.amount_residual_currency, -sm_credit_move.amount_residual_currency)
amount_reconcile = min(sm_debit_move.amount_residual, -sm_credit_move.amount_residual)
self.env['account.partial.reconcile'].create({
'debit_move_id': sm_debit_move.id,
'credit_move_id': sm_credit_move.id,
'amount': amount_reconcile,
'amount_currency': amount_reconcile_currency,
'currency_id': currency,
})
#Iterate process again on self
return self.auto_reconcile_lines()
@api.multi
def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False):
#Perform all checks on lines
company_ids = set()
all_accounts = []
partners = set()
for line in self:
company_ids.add(line.company_id.id)
all_accounts.append(line.account_id)
if (line.account_id.internal_type in ('receivable', 'payable')):
partners.add(line.partner_id.id)
if line.reconciled:
raise UserError(_('You are trying to reconcile some entries that are already reconciled!'))
if len(company_ids) > 1:
raise UserError(_('To reconcile the entries company should be the same for all entries!'))
if len(set(all_accounts)) > 1:
raise UserError(_('Entries are not of the same account!'))
if not all_accounts[0].reconcile:
raise UserError(_('The account %s (%s) is not marked as reconciliable !') % (all_accounts[0].name, all_accounts[0].code))
if len(partners) > 1:
raise UserError(_('The partner has to be the same on all lines for receivable and payable accounts!'))
#reconcile everything that can be
remaining_moves = self.auto_reconcile_lines()
#if writeoff_acc_id specified, then create write-off move with value the remaining amount from move in self
if writeoff_acc_id and writeoff_journal_id and remaining_moves:
writeoff_to_reconcile = remaining_moves._create_writeoff({'account_id': writeoff_acc_id.id, 'journal_id': writeoff_journal_id.id})
#add writeoff line to reconcile algo and finish the reconciliation
remaining_moves = (remaining_moves + writeoff_to_reconcile).auto_reconcile_lines()
def _create_writeoff(self, vals):
""" Create a writeoff move for the account.move.lines in self. If debit/credit is not specified in vals,
the writeoff amount will be computed as the sum of amount_residual of the given recordset.
:param vals: dict containing values suitable fot account_move_line.create(). The data in vals will
be processed to create bot writeoff acount.move.line and their enclosing account.move.
"""
# Check and complete vals
if 'account_id' not in vals or 'journal_id' not in vals:
raise UserError(_("It is mandatory to specify an account and a journal to create a write-off."))
if ('debit' in vals) ^ ('credit' in vals):
raise UserError(_("Either pass both debit and credit or none."))
if 'date' not in vals:
vals['date'] = self._context.get('date_p') or time.strftime('%Y-%m-%d')
if 'name' not in vals:
vals['name'] = self._context.get('comment') or _('Write-Off')
#compute the writeoff amount if not given
if 'credit' not in vals and 'debit' not in vals:
amount = sum([r.amount_residual for r in self])
vals['credit'] = amount > 0 and amount or 0.0
vals['debit'] = amount < 0 and abs(amount) or 0.0
vals['partner_id'] = self.env['res.partner']._find_accounting_partner(self[0].partner_id).id
company_currency = self[0].account_id.company_id.currency_id
account_currency = self[0].account_id.currency_id or company_currency
if 'amount_currency' not in vals and account_currency != company_currency:
vals['currency_id'] = account_currency
vals['amount_currency'] = sum([r.amount_residual_currency for r in self])
# Writeoff line in the account of self
first_line_dict = vals.copy()
first_line_dict['account_id'] = self[0].account_id.id
if 'analytic_account_id' in vals:
del vals['analytic_account_id']
# Writeoff line in specified writeoff account
second_line_dict = vals.copy()
second_line_dict['debit'], second_line_dict['credit'] = second_line_dict['credit'], second_line_dict['debit']
if 'amount_currency' in vals:
second_line_dict['amount_currency'] = -second_line_dict['amount_currency']
# Create the move
writeoff_move = self.env['account.move'].create({
'journal_id': vals['journal_id'],
'date': vals['date'],
'state': 'draft',
'line_ids': [(0, 0, first_line_dict), (0, 0, second_line_dict)],
})
writeoff_move.post()
# Return the writeoff move.line which is to be reconciled
return writeoff_move.line_ids.filtered(lambda r: r.account_id == self[0].account_id)
0 Comment(s)