Skip to content

Commit

Permalink
Merge branch 'github/master' into advance_search_view_master
Browse files Browse the repository at this point in the history
  • Loading branch information
roganw committed Jun 27, 2017
2 parents 3582784 + 6501184 commit 24254fa
Show file tree
Hide file tree
Showing 405 changed files with 5,261 additions and 2,294 deletions.
2 changes: 1 addition & 1 deletion addons/account/__manifest__.py
Expand Up @@ -56,7 +56,7 @@
'views/report_agedpartnerbalance.xml',
'views/tax_adjustments.xml',
'wizard/wizard_tax_adjustments_view.xml',
'views/res_config_view.xml',
'views/account_config_settings_views.xml',
'views/web_planner_data.xml',
'views/account_journal_dashboard_view.xml',
'report/account_report_payment_receipt_templates.xml',
Expand Down
4 changes: 2 additions & 2 deletions addons/account/demo/account_demo.xml
Expand Up @@ -24,13 +24,13 @@
<record id="account_payment_term" model="account.payment.term">
<field name="name">End of Following Month</field>
<field name="note">Payment terms: End of Following Month</field>
<field name="line_ids" eval="[(0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 0, 'option': 'last_day_following_month'})]"/>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 0, 'option': 'last_day_following_month'})]"/>
</record>

<record id="account_payment_term_advance" model="account.payment.term">
<field name="name">30% Advance End of Following Month</field>
<field name="note">Payment terms: 30% Advance End of Following Month</field>
<field name="line_ids" eval="[(0, 0, {'value': 'percent', 'value_amount': 30.0, 'sequence': 400, 'days': 0, 'option': 'day_after_invoice_date'}),
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'percent', 'value_amount': 30.0, 'sequence': 400, 'days': 0, 'option': 'day_after_invoice_date'}),
(0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 0, 'option': 'last_day_following_month'})]"/>
</record>

Expand Down
2 changes: 1 addition & 1 deletion addons/account/models/__init__.py
Expand Up @@ -11,5 +11,5 @@
from . import account_journal_dashboard
from . import product
from . import company
from . import res_config
from . import account_config_settings
from . import web_planner
5 changes: 4 additions & 1 deletion addons/account/models/account.py
Expand Up @@ -523,7 +523,10 @@ def name_get(self):
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
args = args or []
recs = self.search(['|', ('code', operator, name), ('name', operator, name)] + args, limit=limit)
connector = '|'
if operator in expression.NEGATIVE_TERM_OPERATORS:
connector = '&'
recs = self.search([connector, ('code', operator, name), ('name', operator, name)] + args, limit=limit)
return recs.name_get()

@api.multi
Expand Down
2 changes: 1 addition & 1 deletion addons/account/models/account_bank_statement.py
Expand Up @@ -248,7 +248,7 @@ def button_confirm_bank(self):
for aml in st_line.journal_entry_ids:
moves |= aml.move_id
if moves:
moves.post()
moves.filtered(lambda m: m.state != 'posted').post()
statement.message_post(body=_('Statement %s confirmed, journal items were created.') % (statement.name,))
statements.link_bank_to_partner()
statements.write({'state': 'confirm', 'date_done': time.strftime("%Y-%m-%d %H:%M:%S")})
Expand Down
Expand Up @@ -60,34 +60,28 @@ class AccountConfigSettings(models.TransientModel):
module_l10n_eu_service = fields.Boolean(string="EU Digital Goods VAT")

@api.model
def get_default_tax_fields(self, fields):
default_purchase_tax_id = self.env['ir.config_parameter'].sudo().get_param('account.default_purchase_tax_id', default=False)
default_sale_tax_id = self.env['ir.config_parameter'].sudo().get_param('account.default_sale_tax_id', default=False)
return dict(default_purchase_tax_id=int(default_purchase_tax_id), default_sale_tax_id=int(default_sale_tax_id))
def get_values(self):
res = super(AccountConfigSettings, self).get_values()
params = self.env['ir.config_parameter'].sudo()
res.update(
default_purchase_tax_id=params.get_param('account.default_purchase_tax_id', default=False),
default_sale_tax_id=params.get_param('account.default_sale_tax_id', default=False)
)
return res

@api.multi
def set_default_tax_fields(self):
def set_values(self):
super(AccountConfigSettings, self).set_values()
self.env['ir.config_parameter'].sudo().set_param("account.default_purchase_tax_id", self.default_purchase_tax_id.id)
self.env['ir.config_parameter'].sudo().set_param("account.default_sale_tax_id", self.default_sale_tax_id.id)

@api.depends('company_id')
def _compute_has_chart_of_accounts(self):
self.has_chart_of_accounts = bool(self.company_id.chart_template_id)

def set_group_multi_currency(self):
if self.group_multi_currency:
self.env.ref('base.group_user').write({'implied_ids': [(4, self.env.ref('product.group_sale_pricelist').id)]})
return True

def set_default_product_taxes(self):
""" Set the product taxes if they have changed """
ir_values_obj = self.env['ir.values']
if self.default_sale_tax_id:
ir_values_obj.sudo().set_default('product.template', "taxes_id", [self.default_sale_tax_id.id], for_all_users=True, company_id=self.company_id.id)
if self.default_purchase_tax_id:
ir_values_obj.sudo().set_default('product.template', "supplier_taxes_id", [self.default_purchase_tax_id.id], for_all_users=True, company_id=self.company_id.id)

def set_chart_of_accounts(self):
""" install a chart of accounts for the given company (if required) """
if self.chart_template_id and not self.has_chart_of_accounts and self.company_id.expects_chart_of_accounts:
if self.company_id.chart_template_id and self.chart_template_id != self.company_id.chart_template_id:
Expand All @@ -108,6 +102,10 @@ def set_chart_of_accounts(self):
})
wizard.execute()

@api.depends('company_id')
def _compute_has_chart_of_accounts(self):
self.has_chart_of_accounts = bool(self.company_id.chart_template_id)

@api.onchange('group_analytic_accounting')
def onchange_analytic_accounting(self):
if self.group_analytic_accounting:
Expand Down
3 changes: 1 addition & 2 deletions addons/account/models/account_move.py
Expand Up @@ -1322,9 +1322,8 @@ def create_analytic_lines(self):
""" Create analytic items upon validation of an account.move.line having an analytic account. This
method first remove any existing analytic item related to the line before creating any new one.
"""
self.mapped('analytic_line_ids').unlink()
for obj_line in self:
if obj_line.analytic_line_ids:
obj_line.analytic_line_ids.unlink()
if obj_line.analytic_account_id:
vals_line = obj_line._prepare_analytic_line()[0]
self.env['account.analytic.line'].create(vals_line)
Expand Down
179 changes: 119 additions & 60 deletions addons/account/models/account_payment.py
Expand Up @@ -12,9 +12,9 @@
# Since invoice amounts are unsigned, this is how we know if money comes in or goes out
MAP_INVOICE_TYPE_PAYMENT_SIGN = {
'out_invoice': 1,
'in_refund': 1,
'in_refund': -1,
'in_invoice': -1,
'out_refund': -1,
'out_refund': 1,
}

class account_payment_method(models.Model):
Expand Down Expand Up @@ -54,14 +54,17 @@ def _check_amount(self):
if not self.amount > 0.0:
raise ValidationError(_('The payment amount must be strictly positive.'))

@api.one
@api.multi
@api.depends('payment_type', 'journal_id')
def _compute_hide_payment_method(self):
if not self.journal_id:
self.hide_payment_method = True
return
journal_payment_methods = self.payment_type == 'inbound' and self.journal_id.inbound_payment_method_ids or self.journal_id.outbound_payment_method_ids
self.hide_payment_method = len(journal_payment_methods) == 1 and journal_payment_methods[0].code == 'manual'
for payment in self:
if not payment.journal_id:
payment.hide_payment_method = True
continue
journal_payment_methods = payment.payment_type == 'inbound'\
and payment.journal_id.inbound_payment_method_ids\
or payment.journal_id.outbound_payment_method_ids
payment.hide_payment_method = len(journal_payment_methods) == 1 and journal_payment_methods[0].code == 'manual'

@api.onchange('journal_id')
def _onchange_journal(self):
Expand All @@ -75,24 +78,18 @@ def _onchange_journal(self):
return {'domain': {'payment_method_id': [('payment_type', '=', payment_type), ('id', 'in', payment_methods.ids)]}}
return {}

def _get_invoices(self):
""" Return the invoices of the payment. Must be overridden """
raise NotImplementedError

@api.model
def _compute_total_invoices_amount(self):
""" Compute the sum of the residual of invoices, expressed in the payment currency """
payment_currency = self.currency_id or self.journal_id.currency_id or self.journal_id.company_id.currency_id or self.env.user.company_id.currency_id
invoices = self._get_invoices()

if all(inv.currency_id == payment_currency for inv in invoices):
total = sum(invoices.mapped('residual_signed'))
else:
total = 0
for inv in invoices:
if inv.company_currency_id != payment_currency:
total += inv.company_currency_id.with_context(date=self.payment_date).compute(inv.residual_company_signed, payment_currency)
else:
total += inv.residual_company_signed
total = 0
for inv in self.invoice_ids:
if inv.currency_id == payment_currency:
total += inv.residual_company_signed
else:
total += inv.company_currency_id.with_context(date=self.payment_date).compute(
inv.residual_company_signed, payment_currency)
return abs(total)


Expand All @@ -101,71 +98,137 @@ class account_register_payments(models.TransientModel):
_inherit = 'account.abstract.payment'
_description = "Register payments on multiple invoices"

invoice_ids = fields.Many2many('account.invoice', string='Invoices', copy=False)
multi = fields.Boolean(string='Multi', help='Technical field indicating if the user selected invoices from multiple partners or from different types.')

@api.onchange('payment_type')
def _onchange_payment_type(self):
if self.payment_type:
return {'domain': {'payment_method_id': [('payment_type', '=', self.payment_type)]}}

def _get_invoices(self):
return self.env['account.invoice'].browse(self._context.get('active_ids'))
@api.model
def _compute_payment_amount(self, invoice_ids):
payment_currency = self.currency_id or self.journal_id.currency_id or self.journal_id.company_id.currency_id

total = 0
for inv in invoice_ids:
if inv.currency_id == payment_currency:
total += MAP_INVOICE_TYPE_PAYMENT_SIGN[inv.type] * inv.residual_company_signed
else:
amount_residual = inv.company_currency_id.with_context(date=self.payment_date).compute(
inv.residual_company_signed, payment_currency)
total += MAP_INVOICE_TYPE_PAYMENT_SIGN[inv.type] * amount_residual
return total

@api.model
def default_get(self, fields):
rec = super(account_register_payments, self).default_get(fields)
context = dict(self._context or {})
active_model = context.get('active_model')
active_ids = context.get('active_ids')

# Checks on context parameters
if not active_model or not active_ids:
raise UserError(_("Programmation error: wizard action executed without active_model or active_ids in context."))
if active_model != 'account.invoice':
raise UserError(_("Programmation error: the expected model for this action is 'account.invoice'. The provided one is '%d'.") % active_model)

# Checks on received invoice records
invoices = self.env[active_model].browse(active_ids)
active_ids = self._context.get('active_ids')

# Check for selected invoices ids
if not active_ids:
raise UserError(_("Programmation error: wizard action executed without active_ids in context."))

invoices = self.env['account.invoice'].browse(active_ids)

# Check all invoices are open
if any(invoice.state != 'open' for invoice in invoices):
raise UserError(_("You can only register payments for open invoices"))
if any(inv.commercial_partner_id != invoices[0].commercial_partner_id for inv in invoices):
raise UserError(_("In order to pay multiple invoices at once, they must belong to the same commercial partner."))
if any(MAP_INVOICE_TYPE_PARTNER_TYPE[inv.type] != MAP_INVOICE_TYPE_PARTNER_TYPE[invoices[0].type] for inv in invoices):
raise UserError(_("You cannot mix customer invoices and vendor bills in a single payment."))
# Check all invoices have the same currency
if any(inv.currency_id != invoices[0].currency_id for inv in invoices):
raise UserError(_("In order to pay multiple invoices at once, they must use the same currency."))

total_amount = sum(inv.residual * MAP_INVOICE_TYPE_PAYMENT_SIGN[inv.type] for inv in invoices)
communication = ' '.join([ref for ref in invoices.mapped('reference') if ref])
# Look if we are mixin multiple commercial_partner or customer invoices with vendor bills
multi = any(inv.commercial_partner_id != invoices[0].commercial_partner_id
or MAP_INVOICE_TYPE_PARTNER_TYPE[inv.type] != MAP_INVOICE_TYPE_PARTNER_TYPE[invoices[0].type]
for inv in invoices)

total_amount = self._compute_payment_amount(invoices)

rec.update({
'amount': abs(total_amount),
'currency_id': invoices[0].currency_id.id,
'payment_type': total_amount > 0 and 'inbound' or 'outbound',
'partner_id': invoices[0].commercial_partner_id.id,
'partner_type': MAP_INVOICE_TYPE_PARTNER_TYPE[invoices[0].type],
'communication': communication,
'partner_id': False if multi else invoices[0].commercial_partner_id.id,
'partner_type': False if multi else MAP_INVOICE_TYPE_PARTNER_TYPE[invoices[0].type],
'communication': ' '.join([ref for ref in invoices.mapped('reference') if ref]),
'invoice_ids': [(6, 0, invoices.ids)],
'multi': multi,
})
return rec

def get_payment_vals(self):
""" Hook for extension """
@api.multi
def _groupby_invoices(self):
'''Split the invoices linked to the wizard according to their commercial partner and their type.
:return: a dictionary mapping (commercial_partner_id, type) => invoices recordset.
'''
results = {}
# Create a dict dispatching invoices according to their commercial_partner_id and type
for inv in self.invoice_ids:
key = (inv.commercial_partner_id.id, MAP_INVOICE_TYPE_PARTNER_TYPE[inv.type])
if not key in results:
results[key] = self.env['account.invoice']
results[key] += inv
return results

@api.multi
def _prepare_payment_vals(self, invoices):
'''Create the payment values.
:param invoices: The invoices that should have the same commercial partner and the same type.
:return: The payment values as a dictionary.
'''
amount = self._compute_payment_amount(invoices) if self.multi else self.amount
payment_type = ('inbound' if amount > 0 else 'outbound') if self.multi else self.payment_type
return {
'journal_id': self.journal_id.id,
'payment_method_id': self.payment_method_id.id,
'payment_date': self.payment_date,
'communication': self.communication,
'invoice_ids': [(4, inv.id, None) for inv in self._get_invoices()],
'payment_type': self.payment_type,
'amount': self.amount,
'invoice_ids': [(6, 0, invoices.ids)],
'payment_type': payment_type,
'amount': abs(amount),
'currency_id': self.currency_id.id,
'partner_id': self.partner_id.id,
'partner_type': self.partner_type,
'partner_id': invoices[0].commercial_partner_id.id,
'partner_type': MAP_INVOICE_TYPE_PARTNER_TYPE[invoices[0].type],
}

@api.multi
def create_payment(self):
payment = self.env['account.payment'].create(self.get_payment_vals())
payment.post()
return {'type': 'ir.actions.act_window_close'}
def get_payments_vals(self):
'''Compute the values for payments.
:return: a list of payment values (dictionary).
'''
if self.multi:
groups = self._groupby_invoices()
return [self._prepare_payment_vals(invoices) for invoices in groups.values()]
return [self._prepare_payment_vals(self.invoice_ids)]

@api.multi
def create_payments(self):
'''Create payments according to the invoices.
Having invoices with different commercial_partner_id or different type (Vendor bills with customer invoices)
leads to multiple payments.
In case of all the invoices are related to the same commercial_partner_id and have the same type,
only one payment will be created.
:return: The ir.actions.act_window to show created payments.
'''
Payment = self.env['account.payment']
payments = Payment
for payment_vals in self.get_payments_vals():
payments += Payment.create(payment_vals)
payments.post()
return {
'name': _('Payments'),
'domain': [('id', 'in', payments.ids), ('state', '=', 'posted')],
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.payment',
'view_id': False,
'type': 'ir.actions.act_window',
}


class account_payment(models.Model):
Expand Down Expand Up @@ -200,7 +263,6 @@ def _compute_payment_difference(self):
self.payment_difference = self._compute_total_invoices_amount() - self.amount

company_id = fields.Many2one(store=True)

name = fields.Char(readonly=True, copy=False, default="Draft Payment") # The name is attributed upon post()
state = fields.Selection([('draft', 'Draft'), ('posted', 'Posted'), ('sent', 'Sent'), ('reconciled', 'Reconciled'), ('cancel', 'Cancelled')], readonly=True, default='draft', copy=False, string="Status")

Expand Down Expand Up @@ -293,9 +355,6 @@ def default_get(self, fields):
rec['amount'] = invoice['residual']
return rec

def _get_invoices(self):
return self.invoice_ids

@api.multi
def button_journal_entries(self):
return {
Expand Down
2 changes: 1 addition & 1 deletion addons/account/models/chart_template.py
Expand Up @@ -147,7 +147,7 @@ def open_select_template_wizard(self):
todo = self.env['ir.actions.todo']
action_rec = self.env['ir.model.data'].xmlid_to_object('account.action_wizard_multi_chart')
if action_rec:
todo.create({'action_id': action_rec.id, 'name': _('Choose Accounting Template'), 'type': 'automatic'})
todo.create({'action_id': action_rec.id, 'name': _('Choose Accounting Template')})
return True

@api.model
Expand Down

0 comments on commit 24254fa

Please sign in to comment.