1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/partner_commission/models/account_move.py
2024-12-10 09:04:09 +07:00

207 lines
9.1 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from dateutil.relativedelta import relativedelta
from odoo import _, fields, models
from odoo.tools import formatLang, format_date
class AccountMove(models.Model):
_inherit = 'account.move'
referrer_id = fields.Many2one('res.partner', 'Referrer', domain=[('grade_id', '!=', False)], tracking=True)
commission_po_line_id = fields.Many2one('purchase.order.line', 'Referrer Purchase Order line', copy=False)
def _get_sales_representative(self):
self.ensure_one()
# The subscription's Salesperson should be the Purchase Representative.
sub = self.invoice_line_ids.mapped('subscription_id')[:1]
sales_rep = sub and sub.user_id or False
# No subscription: check the sale order's Salesperson.
if not sales_rep:
so = self.invoice_line_ids.mapped('sale_line_ids.order_id')[:1]
sales_rep = so and so.user_id or False
return sales_rep
def _get_commission_purchase_order_domain(self):
self.ensure_one()
domain = [
('partner_id', '=', self.referrer_id.id),
('company_id', '=', self.company_id.id),
('state', '=', 'draft'),
('currency_id', '=', self.currency_id.id),
('purchase_type', '=', 'commission'),
]
sales_rep = self._get_sales_representative()
if sales_rep:
domain += [('user_id', '=', sales_rep.id)]
return domain
def _get_commission_purchase_order(self):
self.ensure_one()
purchase = self.env['purchase.order'].sudo().search(self._get_commission_purchase_order_domain(), limit=1)
if not purchase:
sales_rep = self._get_sales_representative()
purchase = self.env['purchase.order'].with_context(mail_create_nosubscribe=True).sudo().create({
'partner_id': self.referrer_id.id,
'currency_id': self.currency_id.id,
'company_id': self.company_id.id,
'fiscal_position_id': self.env['account.fiscal.position'].with_company(self.company_id)._get_fiscal_position(self.referrer_id).id,
'payment_term_id': self.referrer_id.with_company(self.company_id).property_supplier_payment_term_id.id,
'user_id': sales_rep and sales_rep.id or False,
'dest_address_id': self.referrer_id.id,
'origin': self.name,
'purchase_type': 'commission',
})
return purchase
def _make_commission(self):
for move in self.filtered(lambda m: m.move_type in ['out_invoice', 'in_invoice', 'out_refund']):
if move.move_type in ['out_invoice', 'in_invoice']:
sign = 1
if move.commission_po_line_id or not move.referrer_id:
continue
else:
sign = -1
if not move.commission_po_line_id:
continue
comm_by_rule = defaultdict(float)
product = None
order = None
desc_lines = ""
for line in move.invoice_line_ids:
rule = line._get_commission_rule()
if rule:
if not product:
product = rule.plan_id.product_id
if not order:
order = line.subscription_id
desc_lines += _("\n%s: from %s to %s", line.product_id.name, format_date(self.env, line.deferred_start_date),
format_date(self.env, line.deferred_end_date))
commission = move.currency_id.round(line.price_subtotal * rule.rate / 100.0)
comm_by_rule[rule] += commission
# regulate commissions
for r, amount in comm_by_rule.items():
if r.is_capped:
amount = min(amount, r.max_commission)
comm_by_rule[r] = amount
total = sum(comm_by_rule.values())
if not total:
continue
# build description lines
desc = _(
'Commission on %(invoice)s, %(partner)s, %(amount)s',
invoice=move.name,
partner=move.partner_id.name,
amount=formatLang(self.env, move.amount_untaxed, currency_obj=move.currency_id),
)
if order:
desc += f"\n{order.name}, {desc_lines}"
# extend the description to show the number of months to defer the expense over
end_date_list = move.invoice_line_ids.mapped('deferred_end_date')
start_date_list = move.invoice_line_ids.mapped('deferred_start_date')
if any(start_date_list) and any(end_date_list):
date_to = max((ed for ed in end_date_list if ed))
date_from = min((sd for sd in start_date_list if sd))
# we calculate the delta according to the whole range to avoid 11 month and 29 days= 11 months
delta = relativedelta(date_to + relativedelta(days=1), date_from)
n_months = delta.years * 12 + delta.months + delta.days // 30
if n_months:
desc += _(' (%d month(s))', n_months)
purchase = move._get_commission_purchase_order()
line = self.env['purchase.order.line'].sudo().create({
'name': desc,
'product_id': product.id,
'product_qty': 1,
'price_unit': total * sign,
'product_uom': product.uom_id.id,
'date_planned': fields.Datetime.now(),
'order_id': purchase.id,
'qty_received': 1,
})
if move.move_type in ['out_invoice', 'in_invoice']:
# link the purchase order line to the invoice
move.commission_po_line_id = line
msg_body = _('New commission. Invoice: %s. Amount: %s.',
move._get_html_link(),
formatLang(self.env, total, currency_obj=move.currency_id))
else:
msg_body = _('Commission refunded. Invoice: %s. Amount: %s.',
move._get_html_link(),
formatLang(self.env, total, currency_obj=move.currency_id))
purchase.message_post(body=msg_body)
def _refund_commission(self):
return self._make_commission()
def _reverse_moves(self, default_values_list=None, cancel=False):
if not default_values_list:
default_values_list = [{} for move in self]
for move, default_values in zip(self, default_values_list):
default_values.update({
'referrer_id': move.referrer_id.id,
'commission_po_line_id': move.commission_po_line_id.id,
})
return super(AccountMove, self)._reverse_moves(default_values_list=default_values_list, cancel=cancel)
def _invoice_paid_hook(self):
res = super()._invoice_paid_hook()
self.filtered(lambda move: move.move_type == 'out_refund')._make_commission()
self.filtered(lambda move: move.move_type == 'out_invoice')._make_commission()
return res
def button_draft(self):
res = super(AccountMove, self).button_draft()
for move in self:
cpo = self.env['purchase.order'].sudo().search(move._get_commission_purchase_order_domain(), limit=1)
if cpo and move.move_type == 'out_refund' and cpo.state == 'draft':
message_body = _("The commission partner order %s must be checked manually (especially refund lines which can be duplicated).", cpo._get_html_link())
move.message_post(body=message_body)
return res
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
def _get_commission_rule(self):
self.ensure_one()
template = self.env['sale.order.template']
sale_order = self.subscription_id or self.sale_line_ids.order_id
if len(sale_order) == 1:
template = sale_order.sale_order_template_id
# check whether the product is part of the subscription template
template_products = template.sale_order_template_line_ids.product_id.mapped('product_tmpl_id')
template_id = template.id if template and self.product_id.product_tmpl_id.id in template_products.ids else None
sub_pricelist = self.subscription_id.pricelist_id
pricelist_id = sub_pricelist and sub_pricelist.id or self.sale_line_ids.mapped('order_id.pricelist_id')[:1].id
# In order of precedence, the commission plan can be one of:
# 1. the commission plan set on the subscription
# 2. the commission plan set on the sale order
# 3. the referrer's commission plan
plan = self.sale_line_ids.order_id.commission_plan_id or self.move_id.referrer_id.commission_plan_id
if self.subscription_id:
plan = self.subscription_id.commission_plan_id
if not plan:
return self.env['commission.rule']
return plan._match_rules(self.product_id, template_id, pricelist_id)