Skip to content

Commit

Permalink
[MERGE] forward port branch 12.0 up to cb8fefa
Browse files Browse the repository at this point in the history
  • Loading branch information
KangOl committed Jan 31, 2019
2 parents 24ba5ae + cb8fefa commit f927c68
Show file tree
Hide file tree
Showing 158 changed files with 2,062 additions and 374 deletions.
14 changes: 5 additions & 9 deletions addons/account/models/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,11 @@ def _check_reconcile(self):
@api.constrains('user_type_id')
def _check_user_type_id(self):
data_unaffected_earnings = self.env.ref('account.data_unaffected_earnings')
for company in self.mapped('company_id'):
account_unaffected_earnings = self.search([
('company_id', '=', company.id),
('user_type_id', '=', data_unaffected_earnings.id),
])
if len(account_unaffected_earnings) >= 2:
result = self.read_group([('user_type_id', '=', data_unaffected_earnings.id)], ['company_id'], ['company_id'])
for res in result:
if res.get('company_id_count', 0) >= 2:
account_unaffected_earnings = self.search([('company_id', '=', res['company_id'][0]),
('user_type_id', '=', data_unaffected_earnings.id)])
raise ValidationError(_('You cannot have more than one account with "Current Year Earnings" as type. (accounts: %s)') % [a.code for a in account_unaffected_earnings])

name = fields.Char(required=True, index=True)
Expand Down Expand Up @@ -228,7 +227,6 @@ def onchange_code(self):
self.group_id = group

@api.multi
@api.depends('name', 'code')
def name_get(self):
result = []
for account in self:
Expand Down Expand Up @@ -801,7 +799,6 @@ def set_bank_account(self, acc_number, bank_id=None):
}).id

@api.multi
@api.depends('name', 'currency_id', 'company_id', 'company_id.currency_id')
def name_get(self):
res = []
for journal in self:
Expand Down Expand Up @@ -946,7 +943,6 @@ def copy(self, default=None):
default = dict(default or {}, name=_("%s (Copy)") % self.name)
return super(AccountTax, self).copy(default=default)

@api.depends('name', 'type_tax_use')
def name_get(self):
if not self._context.get('append_type_to_tax_name'):
return super(AccountTax, self).name_get()
Expand Down
10 changes: 10 additions & 0 deletions addons/account/models/account_bank_statement.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,8 @@ def process_reconciliation(self, counterpart_aml_dicts=None, payment_aml_rec=Non
:returns: The journal entries with which the transaction was matched. If there was at least an entry in counterpart_aml_dicts or new_aml_dicts, this list contains
the move created by the reconciliation, containing entries for the statement.line (1), the counterpart move lines (0..*) and the new move lines (0..*).
"""
payable_account_type = self.env.ref('account.data_account_type_payable')
receivable_account_type = self.env.ref('account.data_account_type_receivable')
counterpart_aml_dicts = counterpart_aml_dicts or []
payment_aml_rec = payment_aml_rec or self.env['account.move.line']
new_aml_dicts = new_aml_dicts or []
Expand All @@ -570,10 +572,16 @@ def process_reconciliation(self, counterpart_aml_dicts=None, payment_aml_rec=Non
raise UserError(_('A selected move line was already reconciled.'))
if isinstance(aml_dict['move_line'], int):
aml_dict['move_line'] = aml_obj.browse(aml_dict['move_line'])

account_types = self.env['account.account.type']
for aml_dict in (counterpart_aml_dicts + new_aml_dicts):
if aml_dict.get('tax_ids') and isinstance(aml_dict['tax_ids'][0], int):
# Transform the value in the format required for One2many and Many2many fields
aml_dict['tax_ids'] = [(4, id, None) for id in aml_dict['tax_ids']]

user_type_id = self.env['account.account'].browse(aml_dict.get('account_id')).user_type_id
if user_type_id in [payable_account_type, receivable_account_type] and user_type_id not in account_types:
account_types |= user_type_id
if any(line.journal_entry_ids for line in self):
raise UserError(_('A selected statement line was already reconciled with an account move.'))

Expand Down Expand Up @@ -609,6 +617,8 @@ def process_reconciliation(self, counterpart_aml_dicts=None, payment_aml_rec=Non
payment = self.env['account.payment']
if abs(total)>0.00001:
payment_vals = self._prepare_payment_vals(total)
if self.partner_id and len(account_types) == 1:
payment_vals['partner_type'] = 'customer' if account_types == receivable_account_type else 'supplier'
payment = payment.create(payment_vals)

# Complete dicts to create both counterpart move lines and write-offs
Expand Down
26 changes: 25 additions & 1 deletion addons/account/models/account_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,13 @@ def _get_outstanding_info_JSON(self):
amount_to_show = currency._convert(abs(line.amount_residual), self.currency_id, self.company_id, line.date or fields.Date.today())
if float_is_zero(amount_to_show, precision_rounding=self.currency_id.rounding):
continue
if line.ref :
title = '%s : %s' % (line.move_id.name, line.ref)
else:
title = line.move_id.name
info['content'].append({
'journal_name': line.ref or line.move_id.name,
'title': title,
'amount': amount_to_show,
'currency': currency_id.symbol,
'id': line.id,
Expand Down Expand Up @@ -1402,6 +1407,20 @@ def _refund_cleanup_lines(self, lines):
return result

@api.model
def _refund_tax_lines_account_change(self, lines, taxes_to_change):
# Let's change the account on tax lines when
# @param {list} lines: a list of orm commands
# @param {dict} taxes_to_change
# key: tax ID, value: refund account

if not taxes_to_change:
return lines

for line in lines:
if isinstance(line[2], dict) and line[2]['tax_id'] in taxes_to_change:
line[2]['account_id'] = taxes_to_change[line[2]['tax_id']]
return lines

def _get_refund_common_fields(self):
return ['partner_id', 'payment_term_id', 'account_id', 'currency_id', 'journal_id']

Expand Down Expand Up @@ -1447,7 +1466,12 @@ def _prepare_refund(self, invoice, date_invoice=None, date=None, description=Non
values['invoice_line_ids'] = self._refund_cleanup_lines(invoice.invoice_line_ids)

tax_lines = invoice.tax_line_ids
values['tax_line_ids'] = self._refund_cleanup_lines(tax_lines)
taxes_to_change = {
line.tax_id.id: line.tax_id.refund_account_id.id
for line in tax_lines.filtered(lambda l: l.tax_id.refund_account_id != l.tax_id.account_id)
}
cleaned_tax_lines = self._refund_cleanup_lines(tax_lines)
values['tax_line_ids'] = self._refund_tax_lines_account_change(cleaned_tax_lines, taxes_to_change)

if journal_id:
journal = self.env['account.journal'].browse(journal_id)
Expand Down
47 changes: 35 additions & 12 deletions addons/account/models/account_journal_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,14 @@ def get_journal_dashboard_datas(self):
self.env.cr.execute(query, query_args)
query_results_drafts = self.env.cr.dictfetchall()

today = datetime.today()
query = """SELECT amount_total, currency_id AS currency, type, date_invoice, company_id FROM account_invoice WHERE journal_id = %s AND date <= %s AND state = 'open';"""
today = fields.Date.today()
query = """SELECT residual_signed as amount_total, currency_id AS currency, type, date_invoice, company_id FROM account_invoice WHERE journal_id = %s AND date <= %s AND state = 'open';"""
self.env.cr.execute(query, (self.id, today))
late_query_results = self.env.cr.dictfetchall()
(number_waiting, sum_waiting) = self._count_results_and_sum_amounts(query_results_to_pay, currency)
(number_draft, sum_draft) = self._count_results_and_sum_amounts(query_results_drafts, currency)
(number_late, sum_late) = self._count_results_and_sum_amounts(late_query_results, currency)
curr_cache = {}
(number_waiting, sum_waiting) = self._count_results_and_sum_amounts(query_results_to_pay, currency, curr_cache=curr_cache)
(number_draft, sum_draft) = self._count_results_and_sum_amounts(query_results_drafts, currency, curr_cache=curr_cache)
(number_late, sum_late) = self._count_results_and_sum_amounts(late_query_results, currency, curr_cache=curr_cache)

difference = currency.round(last_balance-account_sum) + 0.0
return {
Expand Down Expand Up @@ -226,23 +227,45 @@ def _get_draft_bills_query(self):
gather the bills in draft state data, and the arguments
dictionary to use to run it as its second.
"""
return ("""SELECT state, amount_total, currency_id AS currency, type, date_invoice, company_id
FROM account_invoice
# there is no account_move_lines for draft invoices, so no relevant residual_signed value
return ("""SELECT state,
(CASE WHEN inv.type in ('out_invoice', 'in_invoice')
THEN inv.amount_total
ELSE (-1 * inv.amount_total)
END) AS amount_total,
inv.currency_id AS currency,
inv.type,
inv.date_invoice,
inv.company_id
FROM account_invoice inv
WHERE journal_id = %(journal_id)s AND state = 'draft';""", {'journal_id':self.id})

def _count_results_and_sum_amounts(self, results_dict, target_currency):
def _count_results_and_sum_amounts(self, results_dict, target_currency, curr_cache=None):
""" Loops on a query result to count the total number of invoices and sum
their amount_total field (expressed in the given target currency).
amount_total must be signed !
"""
rslt_count = 0
rslt_sum = 0.0
# Create a cache with currency rates to avoid unnecessary SQL requests. Do not copy
# curr_cache on purpose, so the dictionary is modified and can be re-used for subsequent
# calls of the method.
curr_cache = {} if curr_cache is None else curr_cache
for result in results_dict:
cur = self.env['res.currency'].browse(result.get('currency'))
company = self.env['res.company'].browse(result.get('company_id')) or self.env.user.company_id
rslt_count += 1
type_factor = result.get('type') in ('in_refund', 'out_refund') and -1 or 1
rslt_sum += type_factor * cur._convert(
result.get('amount_total'), target_currency, company, result.get('date_invoice') or fields.Date.today())
date = result.get('date_invoice') or fields.Date.today()

amount = result.get('amount_total', 0)
if cur != target_currency:
key = (cur, target_currency, company, date)
# Using setdefault will call _get_conversion_rate, so we explicitly check the
# existence of the key in the cache instead.
if key not in curr_cache:
curr_cache[key] = self.env['res.currency']._get_conversion_rate(*key)
amount *= curr_cache[key]
rslt_sum += target_currency.round(amount)
return (rslt_count, rslt_sum)

@api.multi
Expand Down Expand Up @@ -360,7 +383,7 @@ def open_action(self):
action['view_id'] = False
if self.type == 'purchase':
new_help = self.env['account.invoice'].with_context(ctx).complete_empty_list_help()
action.update({'help': action.get('help', '') + new_help})
action.update({'help': (action.get('help') or '') + new_help})
return action

@api.multi
Expand Down
43 changes: 26 additions & 17 deletions addons/account/models/account_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,6 @@ def reverse_moves(self, date=None, journal_id=None, auto=False):
journal_id=journal_id,
auto=auto)
reversed_moves |= reversed_move
#unreconcile all lines reversed
aml = ac_move.line_ids.filtered(lambda x: x.account_id.reconcile or x.account_id.internal_type == 'liquidity')
aml.remove_move_reconcile()
#reconcile together the reconcilable (or the liquidity aml) and their newly created counterpart
for account in set([x.account_id for x in aml]):
to_rec = aml.filtered(lambda y: y.account_id == account)
Expand All @@ -429,6 +426,18 @@ def reverse_moves(self, date=None, journal_id=None, auto=False):
def open_reconcile_view(self):
return self.line_ids.open_reconcile_view()

# FIXME: Clarify me and change me in master
@api.multi
def action_duplicate(self):
self.ensure_one()
action = self.env.ref('account.action_move_journal_line').read()[0]
action['target'] = 'inline'
action['context'] = dict(self.env.context)
action['context']['view_no_maturity'] = False
action['views'] = [(self.env.ref('account.view_move_form').id, 'form')]
action['res_id'] = self.copy().id
return action

@api.model
def _run_reverses_entries(self):
''' This method is called from a cron job. '''
Expand Down Expand Up @@ -618,7 +627,7 @@ def _get_counterpart(self):
index=True, store=True, copy=False) # related is required
blocked = fields.Boolean(string='No Follow-up', default=False,
help="You can check this box to mark this journal item as a litigation with the associated partner")
date_maturity = fields.Date(string='Due date', index=True, required=True,
date_maturity = fields.Date(string='Due date', index=True, required=True, copy=False,
help="This field is used for payable and receivable journal entries. You can put the limit date for the payment of this line.")
date = fields.Date(related='move_id.date', string='Date', index=True, store=True, copy=False, readonly=False) # related is required
analytic_line_ids = fields.One2many('account.analytic.line', 'move_id', string='Analytic lines', oldname="analytic_lines")
Expand Down Expand Up @@ -877,22 +886,13 @@ def auto_reconcile_lines(self):
ret = self._reconcile_lines(debit_moves, credit_moves, field)
return ret

@api.multi
def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False):
# Empty self can happen if the user tries to reconcile entries which are already reconciled.
# The calling method might have filtered out reconciled lines.
if not self:
return True

def _check_reconcile_validity(self):
#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:
Expand All @@ -902,6 +902,14 @@ def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False):
if not (all_accounts[0].reconcile or all_accounts[0].internal_type == 'liquidity'):
raise UserError(_('Account %s (%s) does not allow reconciliation. First change the configuration of this account to allow it.') % (all_accounts[0].name, all_accounts[0].code))

@api.multi
def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False):
# Empty self can happen if the user tries to reconcile entries which are already reconciled.
# The calling method might have filtered out reconciled lines.
if not self:
return True

self._check_reconcile_validity()
#reconcile everything that can be
remaining_moves = self.auto_reconcile_lines()

Expand All @@ -919,7 +927,7 @@ def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False):
#add writeoff line to reconcile algorithm and finish the reconciliation
remaining_moves = (remaining_moves + writeoff_to_reconcile).auto_reconcile_lines()
# Check if reconciliation is total or needs an exchange rate entry to be created
(self+writeoff_to_reconcile).check_full_reconcile()
(self + writeoff_to_reconcile).check_full_reconcile()
return True

def _create_writeoff(self, writeoff_vals):
Expand Down Expand Up @@ -963,8 +971,9 @@ def compute_writeoff_counterpart_vals(values):
raise UserError(_("Either pass both debit and credit or none."))
if 'date' not in vals:
vals['date'] = self._context.get('date_p') or fields.Date.today()
if vals['date'] < date:
date = vals['date']
vals['date'] = fields.Date.to_date(vals['date'])
if vals['date'] and vals['date'] < date:
date = vals['date']
if 'name' not in vals:
vals['name'] = self._context.get('comment') or _('Write-Off')
if 'analytic_account_id' not in vals:
Expand Down
9 changes: 8 additions & 1 deletion addons/account/models/account_payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def _prepare_payment_vals(self, invoices):
pmt_communication = self.show_communication_field and self.communication \
or self.group_invoices and ' '.join([inv.reference or inv.number for inv in invoices]) \
or invoices[0].reference # in this case, invoices contains only one element, since group_invoices is False
return {
values = {
'journal_id': self.journal_id.id,
'payment_method_id': self.payment_method_id.id,
'payment_date': self.payment_date,
Expand All @@ -337,8 +337,13 @@ def _prepare_payment_vals(self, invoices):
'partner_type': MAP_INVOICE_TYPE_PARTNER_TYPE[invoices[0].type],
'partner_bank_account_id': bank_account.id,
'multi': False,
'payment_difference_handling': self.payment_difference_handling,
'writeoff_account_id': self.writeoff_account_id.id,
'writeoff_label': self.writeoff_label,
}

return values

@api.multi
def get_payments_vals(self):
'''Compute the values for payments.
Expand Down Expand Up @@ -429,6 +434,8 @@ def open_payment_matching_screen(self):
if move_line.account_id.reconcile:
move_line_id = move_line.id
break;
if not self.partner_id:
raise UserError(_("Payments without a customer can't be matched"))
action_context = {'company_ids': [self.company_id.id], 'partner_ids': [self.partner_id.commercial_partner_id.id]}
if self.partner_type == 'customer':
action_context.update({'mode': 'customers'})
Expand Down
2 changes: 1 addition & 1 deletion addons/account/models/chart_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def try_loading_for_current_company(self):
# (won't work well for multi-company)
company = self.env.user.company_id
# If we don't have any chart of account on this company, install this chart of account
if not company.chart_template_id:
if not self.existing_accounting(company):
self.load_for_current_company(15.0, 15.0)

def load_for_current_company(self, sale_tax_rate, purchase_tax_rate):
Expand Down
7 changes: 6 additions & 1 deletion addons/account/models/company.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,13 @@ def get_unaffected_earnings_account(self):
('user_type_id', '=', unaffected_earnings_type.id)])
if account:
return account[0]
# Do not assume '999999' doesn't exist since the user might have created such an account
# manually.
code = 999999
while self.env['account.account'].search([('code', '=', str(code)), ('company_id', '=', self.id)]):
code -= 1
return self.env['account.account'].create({
'code': '999999',
'code': str(code),
'name': _('Undistributed Profits/Losses'),
'user_type_id': unaffected_earnings_type.id,
'company_id': self.id,
Expand Down
Loading

0 comments on commit f927c68

Please sign in to comment.