[FIX] (sale_)project(_purchase): proj.prof. w/ AA distr. for inv/bills
Current behaviour: The project profitability panel doesn't take into account the AA distribution of the project's AA for solo invoices and bills. Expected behaviour: It should take the distribution set into account. Steps to reproduce: - Install Accounting, Sales, Project, Purchase - Settings > Activate AA - Create a new project with a new AA for it. - Create a new bill or invoice, on the line set the 2 AA, one with the project's AA with some X%, and the other AA is irrelevant (the sum of all AA on the line should be 100%) - Confirm the invoice/bill - Go to the project profitability report (project updates), see that we have 100% of the line value that contributed, when it should have. Reason for the problem: The contribution of the AA was not taken into account when computing the project profitability for solo invoices/bills (invoices/bills w/o SO/PO respectively) Fix: Additionally get the analytic_distribution when fetch the account_move_lines to compute the project's profitability. Affected versions: - 16.0 - saas-16.1 - saas-16.2 - master opw-3246199 closes odoo/odoo#117163 X-original-commit: c3c66d05d039514c9dd02ebdede20a3dff757728 Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com> Signed-off-by: Piryns Victor (pivi) <pivi@odoo.com>
This commit is contained in:
@@ -177,7 +177,7 @@ class Project(models.Model):
|
||||
# account_move_line__move_id is the alias of the joined table account_move in the query
|
||||
# we can use it, because of the "move_id.move_type" clause in the domain of the query, which generates the join
|
||||
# this is faster than a search_read followed by a browse on the move_id to retrieve the move_type of each account.move.line
|
||||
query_string, query_param = query.select('price_subtotal', 'parent_state', 'account_move_line.currency_id', 'account_move_line__move_id.move_type')
|
||||
query_string, query_param = query.select('price_subtotal', 'parent_state', 'account_move_line.currency_id', 'account_move_line.analytic_distribution', 'account_move_line__move_id.move_type')
|
||||
self._cr.execute(query_string, query_param)
|
||||
bills_move_line_read = self._cr.dictfetchall()
|
||||
if bills_move_line_read:
|
||||
@@ -190,16 +190,17 @@ class Project(models.Model):
|
||||
amount_invoiced = amount_to_invoice = 0.0
|
||||
for moves_read in bills_move_line_read:
|
||||
price_subtotal = self.analytic_account_id.currency_id.round(moves_read['price_subtotal'] * conversion_rates[moves_read['currency_id']])
|
||||
analytic_contribution = moves_read['analytic_distribution'][str(self.analytic_account_id.id)] / 100.
|
||||
if moves_read['parent_state'] == 'draft':
|
||||
if moves_read['move_type'] == 'in_invoice':
|
||||
amount_to_invoice -= price_subtotal
|
||||
amount_to_invoice -= price_subtotal * analytic_contribution
|
||||
else: # moves_read['move_type'] == 'in_refund'
|
||||
amount_to_invoice += price_subtotal
|
||||
amount_to_invoice += price_subtotal * analytic_contribution
|
||||
else: # moves_read['parent_state'] == 'posted'
|
||||
if moves_read['move_type'] == 'in_invoice':
|
||||
amount_invoiced -= price_subtotal
|
||||
amount_invoiced -= price_subtotal * analytic_contribution
|
||||
else: # moves_read['move_type'] == 'in_refund'
|
||||
amount_invoiced += price_subtotal
|
||||
amount_invoiced += price_subtotal * analytic_contribution
|
||||
# don't display the section if the final values are both 0 (bill -> vendor credit)
|
||||
if amount_invoiced != 0 or amount_to_invoice != 0:
|
||||
costs = profitability_items['costs']
|
||||
|
||||
@@ -17,7 +17,12 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
"""
|
||||
A bill that has an AAL on one of its line should be taken into account
|
||||
for the profitability of the project.
|
||||
The contribution of the line should only be dependent
|
||||
on the project's analytic account % that was set on the line
|
||||
"""
|
||||
# a custom analytic contribution (number between 1 -> 100 included)
|
||||
analytic_distribution = 42
|
||||
analytic_contribution = analytic_distribution / 100.
|
||||
# create a bill_1 with the AAL
|
||||
bill_1 = self.env['account.move'].create({
|
||||
"name": "Bill_1 name",
|
||||
@@ -26,7 +31,7 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
"partner_id": self.partner.id,
|
||||
"invoice_date": datetime.today(),
|
||||
"invoice_line_ids": [Command.create({
|
||||
"analytic_distribution": {self.analytic_account.id: 100},
|
||||
"analytic_distribution": {self.analytic_account.id: analytic_distribution},
|
||||
"product_id": self.product_a.id,
|
||||
"quantity": 1,
|
||||
"product_uom_id": self.product_a.uom_id.id,
|
||||
@@ -41,10 +46,10 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
'data': [{
|
||||
'id': 'other_purchase_costs',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_purchase_costs'],
|
||||
'to_bill': -self.product_a.standard_price,
|
||||
'to_bill': -self.product_a.standard_price * analytic_contribution,
|
||||
'billed': 0.0,
|
||||
}],
|
||||
'total': {'to_bill': -self.product_a.standard_price, 'billed': 0.0},
|
||||
'total': {'to_bill': -self.product_a.standard_price * analytic_contribution, 'billed': 0.0},
|
||||
},
|
||||
)
|
||||
# post bill_1
|
||||
@@ -57,9 +62,9 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
'id': 'other_purchase_costs',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_purchase_costs'],
|
||||
'to_bill': 0.0,
|
||||
'billed': -self.product_a.standard_price,
|
||||
'billed': -self.product_a.standard_price * analytic_contribution,
|
||||
}],
|
||||
'total': {'to_bill': 0.0, 'billed': -self.product_a.standard_price},
|
||||
'total': {'to_bill': 0.0, 'billed': -self.product_a.standard_price * analytic_contribution},
|
||||
},
|
||||
)
|
||||
# create another bill, with 2 lines, 2 diff products, the second line has 2 as quantity
|
||||
@@ -70,14 +75,14 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
"partner_id": self.partner.id,
|
||||
"invoice_date": datetime.today(),
|
||||
"invoice_line_ids": [Command.create({
|
||||
"analytic_distribution": {self.analytic_account.id: 100},
|
||||
"analytic_distribution": {self.analytic_account.id: analytic_distribution},
|
||||
"product_id": self.product_a.id,
|
||||
"quantity": 1,
|
||||
"product_uom_id": self.product_a.uom_id.id,
|
||||
"price_unit": self.product_a.standard_price,
|
||||
"currency_id": self.env.company.currency_id.id,
|
||||
}), Command.create({
|
||||
"analytic_distribution": {self.analytic_account.id: 100},
|
||||
"analytic_distribution": {self.analytic_account.id: analytic_distribution},
|
||||
"product_id": self.product_b.id,
|
||||
"quantity": 2,
|
||||
"product_uom_id": self.product_b.uom_id.id,
|
||||
@@ -92,12 +97,12 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
'data': [{
|
||||
'id': 'other_purchase_costs',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_purchase_costs'],
|
||||
'to_bill': -(self.product_a.standard_price + 2 * self.product_b.standard_price),
|
||||
'billed': -self.product_a.standard_price,
|
||||
'to_bill': -(self.product_a.standard_price + 2 * self.product_b.standard_price) * analytic_contribution,
|
||||
'billed': -self.product_a.standard_price * analytic_contribution,
|
||||
}],
|
||||
'total': {
|
||||
'to_bill': -(self.product_a.standard_price + 2 * self.product_b.standard_price),
|
||||
'billed': -self.product_a.standard_price,
|
||||
'to_bill': -(self.product_a.standard_price + 2 * self.product_b.standard_price) * analytic_contribution,
|
||||
'billed': -self.product_a.standard_price * analytic_contribution,
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -111,11 +116,11 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
'id': 'other_purchase_costs',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_purchase_costs'],
|
||||
'to_bill': 0.0,
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price),
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price) * analytic_contribution,
|
||||
}],
|
||||
'total': {
|
||||
'to_bill': 0.0,
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price),
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price) * analytic_contribution,
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -124,7 +129,7 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
"name": "A purchase order",
|
||||
"partner_id": self.partner_a.id,
|
||||
"order_line": [Command.create({
|
||||
"analytic_distribution": {self.analytic_account.id: 100},
|
||||
"analytic_distribution": {self.analytic_account.id: analytic_distribution},
|
||||
"product_id": self.product_order.id,
|
||||
"product_qty": 1,
|
||||
"price_unit": self.product_order.standard_price,
|
||||
@@ -142,17 +147,17 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
'data': [{
|
||||
'id': 'purchase_order',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['purchase_order'],
|
||||
'to_bill': -self.product_order.standard_price,
|
||||
'to_bill': -self.product_order.standard_price * analytic_contribution,
|
||||
'billed': 0.0,
|
||||
}, {
|
||||
'id': 'other_purchase_costs',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_purchase_costs'],
|
||||
'to_bill': 0.0,
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price),
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price) * analytic_contribution,
|
||||
}],
|
||||
'total': {
|
||||
'to_bill': -self.product_order.standard_price,
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price),
|
||||
'to_bill': -self.product_order.standard_price * analytic_contribution,
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price) * analytic_contribution,
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -169,18 +174,18 @@ class TestProjectPurchaseProfitability(TestProjectProfitabilityCommon, TestPurch
|
||||
'id': 'purchase_order',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['purchase_order'],
|
||||
'to_bill': 0.0,
|
||||
'billed': -self.product_order.standard_price,
|
||||
'billed': -self.product_order.standard_price * analytic_contribution,
|
||||
}, {
|
||||
'id': 'other_purchase_costs',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_purchase_costs'],
|
||||
'to_bill': 0.0,
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price),
|
||||
'billed': -2 * (self.product_a.standard_price + self.product_b.standard_price) * analytic_contribution,
|
||||
}],
|
||||
'total': {
|
||||
'to_bill': 0.0,
|
||||
'billed': -(2 * self.product_a.standard_price +
|
||||
2 * self.product_b.standard_price +
|
||||
self.product_order.standard_price),
|
||||
self.product_order.standard_price) * analytic_contribution,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -434,7 +434,7 @@ class Project(models.Model):
|
||||
# account_move_line__move_id is the alias of the joined table account_move in the query
|
||||
# we can use it, because of the "move_id.move_type" clause in the domain of the query, which generates the join
|
||||
# this is faster than a search_read followed by a browse on the move_id to retrieve the move_type of each account.move.line
|
||||
query_string, query_param = query.select('price_subtotal', 'parent_state', 'account_move_line.currency_id', 'account_move_line__move_id.move_type')
|
||||
query_string, query_param = query.select('price_subtotal', 'parent_state', 'account_move_line.currency_id', 'account_move_line.analytic_distribution', 'account_move_line__move_id.move_type')
|
||||
self._cr.execute(query_string, query_param)
|
||||
invoices_move_line_read = self._cr.dictfetchall()
|
||||
if invoices_move_line_read:
|
||||
@@ -447,16 +447,17 @@ class Project(models.Model):
|
||||
amount_invoiced = amount_to_invoice = 0.0
|
||||
for moves_read in invoices_move_line_read:
|
||||
price_subtotal = self.analytic_account_id.currency_id.round(moves_read['price_subtotal'] * conversion_rates[moves_read['currency_id']])
|
||||
analytic_contribution = moves_read['analytic_distribution'][str(self.analytic_account_id.id)] / 100.
|
||||
if moves_read['parent_state'] == 'draft':
|
||||
if moves_read['move_type'] == 'out_invoice':
|
||||
amount_to_invoice += price_subtotal
|
||||
amount_to_invoice += price_subtotal * analytic_contribution
|
||||
else: # moves_read['move_type'] == 'out_refund'
|
||||
amount_to_invoice -= price_subtotal
|
||||
amount_to_invoice -= price_subtotal * analytic_contribution
|
||||
else: # moves_read['parent_state'] == 'posted'
|
||||
if moves_read['move_type'] == 'out_invoice':
|
||||
amount_invoiced += price_subtotal
|
||||
amount_invoiced += price_subtotal * analytic_contribution
|
||||
else: # moves_read['move_type'] == 'out_refund'
|
||||
amount_invoiced -= price_subtotal
|
||||
amount_invoiced -= price_subtotal * analytic_contribution
|
||||
# don't display the section if the final values are both 0 (invoice -> credit note)
|
||||
if amount_invoiced != 0 or amount_to_invoice != 0:
|
||||
section_id = 'other_invoice_revenues'
|
||||
|
||||
@@ -245,8 +245,13 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo
|
||||
"""
|
||||
An invoice that has an AAL on one of its line should be taken into account
|
||||
for the profitability of the project.
|
||||
The contribution of the line should only be dependent
|
||||
on the project's analytic account % that was set on the line
|
||||
"""
|
||||
self.project.allow_billable = True
|
||||
# a custom analytic contribution (number between 1 -> 100 included)
|
||||
analytic_distribution = 42
|
||||
analytic_contribution = analytic_distribution / 100.
|
||||
# create a invoice_1 with the AAL
|
||||
invoice_1 = self.env['account.move'].create({
|
||||
"name": "Invoice_1",
|
||||
@@ -255,7 +260,7 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo
|
||||
"partner_id": self.partner.id,
|
||||
"invoice_date": datetime.today(),
|
||||
"invoice_line_ids": [Command.create({
|
||||
"analytic_distribution": {self.analytic_account.id: 100},
|
||||
"analytic_distribution": {self.analytic_account.id: analytic_distribution},
|
||||
"product_id": self.product_a.id,
|
||||
"quantity": 1,
|
||||
"product_uom_id": self.product_a.uom_id.id,
|
||||
@@ -269,10 +274,10 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo
|
||||
'data': [{
|
||||
'id': 'other_invoice_revenues',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_invoice_revenues'],
|
||||
'to_invoice': self.product_a.standard_price,
|
||||
'to_invoice': self.product_a.standard_price * analytic_contribution,
|
||||
'invoiced': 0.0,
|
||||
}],
|
||||
'total': {'to_invoice': self.product_a.standard_price, 'invoiced': 0.0},
|
||||
'total': {'to_invoice': self.product_a.standard_price * analytic_contribution, 'invoiced': 0.0},
|
||||
},
|
||||
)
|
||||
# post invoice_1
|
||||
@@ -285,9 +290,9 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo
|
||||
'id': 'other_invoice_revenues',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_invoice_revenues'],
|
||||
'to_invoice': 0.0,
|
||||
'invoiced': self.product_a.standard_price,
|
||||
'invoiced': self.product_a.standard_price * analytic_contribution,
|
||||
}],
|
||||
'total': {'to_invoice': 0.0, 'invoiced': self.product_a.standard_price},
|
||||
'total': {'to_invoice': 0.0, 'invoiced': self.product_a.standard_price * analytic_contribution},
|
||||
},
|
||||
)
|
||||
# create another invoice, with 2 lines, 2 diff products, the second line has 2 as quantity
|
||||
@@ -298,13 +303,13 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo
|
||||
"partner_id": self.partner.id,
|
||||
"invoice_date": datetime.today(),
|
||||
"invoice_line_ids": [Command.create({
|
||||
"analytic_distribution": {self.analytic_account.id: 100},
|
||||
"analytic_distribution": {self.analytic_account.id: analytic_distribution},
|
||||
"product_id": self.product_a.id,
|
||||
"quantity": 1,
|
||||
"product_uom_id": self.product_a.uom_id.id,
|
||||
"price_unit": self.product_a.standard_price,
|
||||
}), Command.create({
|
||||
"analytic_distribution": {self.analytic_account.id: 100},
|
||||
"analytic_distribution": {self.analytic_account.id: analytic_distribution},
|
||||
"product_id": self.product_b.id,
|
||||
"quantity": 2,
|
||||
"product_uom_id": self.product_b.uom_id.id,
|
||||
@@ -318,12 +323,12 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo
|
||||
'data': [{
|
||||
'id': 'other_invoice_revenues',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_invoice_revenues'],
|
||||
'to_invoice': (self.product_a.standard_price + 2 * self.product_b.standard_price),
|
||||
'invoiced': self.product_a.standard_price,
|
||||
'to_invoice': (self.product_a.standard_price + 2 * self.product_b.standard_price) * analytic_contribution,
|
||||
'invoiced': self.product_a.standard_price * analytic_contribution,
|
||||
}],
|
||||
'total': {
|
||||
'to_invoice': (self.product_a.standard_price + 2 * self.product_b.standard_price),
|
||||
'invoiced': self.product_a.standard_price,
|
||||
'to_invoice': (self.product_a.standard_price + 2 * self.product_b.standard_price) * analytic_contribution,
|
||||
'invoiced': self.product_a.standard_price * analytic_contribution,
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -337,11 +342,11 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo
|
||||
'id': 'other_invoice_revenues',
|
||||
'sequence': self.project._get_profitability_sequence_per_invoice_type()['other_invoice_revenues'],
|
||||
'to_invoice': 0.0,
|
||||
'invoiced': 2 * (self.product_a.standard_price + self.product_b.standard_price),
|
||||
'invoiced': 2 * (self.product_a.standard_price + self.product_b.standard_price) * analytic_contribution,
|
||||
}],
|
||||
'total': {
|
||||
'to_invoice': 0.0,
|
||||
'invoiced': 2 * (self.product_a.standard_price + self.product_b.standard_price),
|
||||
'invoiced': 2 * (self.product_a.standard_price + self.product_b.standard_price) * analytic_contribution,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user