diff --git a/odex25_purchase/purchase_budget_fix/__init__.py b/odex25_purchase/purchase_budget_fix/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/odex25_purchase/purchase_budget_fix/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/odex25_purchase/purchase_budget_fix/__manifest__.py b/odex25_purchase/purchase_budget_fix/__manifest__.py new file mode 100644 index 000000000..9d717e42c --- /dev/null +++ b/odex25_purchase/purchase_budget_fix/__manifest__.py @@ -0,0 +1,15 @@ +{ + 'name': 'Purchase Budget Fix', + 'version': '14.0.1.0.0', + 'description': '', + 'summary': 'Fix and enhance budget validation logic in Purchase Orders', + 'author': '', + 'website': '', + 'license': 'LGPL-3', + 'category': 'purchase', + 'depends': ['base',"purchase_requisition_custom"], + 'data': [ + # 'security/ir.model.access.csv', + + ], +} \ No newline at end of file diff --git a/odex25_purchase/purchase_budget_fix/models/__init__.py b/odex25_purchase/purchase_budget_fix/models/__init__.py new file mode 100644 index 000000000..9186ee3ad --- /dev/null +++ b/odex25_purchase/purchase_budget_fix/models/__init__.py @@ -0,0 +1 @@ +from . import model diff --git a/odex25_purchase/purchase_budget_fix/models/model.py b/odex25_purchase/purchase_budget_fix/models/model.py new file mode 100644 index 000000000..0fe7b25f9 --- /dev/null +++ b/odex25_purchase/purchase_budget_fix/models/model.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +from odoo import models, _, api +from odoo.exceptions import ValidationError + +class PurchaseOrder(models.Model): + _inherit = 'purchase.order' + + def action_budget(self): + confirmation_lines = [] + amount = 0 + + if self.amount_total <= 0: + raise ValidationError(_("Total Amount MUST be greater than 0 !!!")) + + analytic_account = False + if self.purchase_cost == 'default': + analytic_account = self.env.user.company_id.purchase_analytic_account + elif self.purchase_cost == 'department': + analytic_account = self.department_id.analytic_account_id + elif self.purchase_cost == 'project': + analytic_account = self.project_id.analytic_account_id + if not analytic_account: + raise ValidationError(_("No analytic account for the project")) + elif self.purchase_cost == 'product_line': + pass + else: + raise ValidationError(_("No analytic account for the purchase")) + + for order in self: + for rec in order.order_line: + if rec.choosen: + if self.purchase_cost == 'product_line': + analytic_account = rec.account_analytic_id + if not analytic_account: + raise ValidationError( + _("Please put cost center to the product line") + ': {}'.format(rec.product_id.name) + ) + + account_id = rec.product_id.property_account_expense_id or rec.product_id.categ_id.property_account_expense_categ_id + if not account_id: + raise ValidationError( + _("This product has no expense account") + ': {}'.format(rec.product_id.name) + ) + + if not analytic_account: + raise ValidationError(_("Analytic account not set")) + + budget_lines = self.env['crossovered.budget.lines'].search([ + ('analytic_account_id', '=', analytic_account.id), + ('crossovered_budget_id.state', '=', 'done'), + ('crossovered_budget_id.date_from', '<=', self.date_order), + ('crossovered_budget_id.date_to', '>=', self.date_order) + ]) + + if not budget_lines: + raise ValidationError( + _("No approved budget found for analytic account: %s on date: %s") + % (analytic_account.name, self.date_order) + ) + + valid_budget_positions = budget_lines.mapped('general_budget_id').filtered( + lambda x: account_id in x.account_ids + ) + + if not valid_budget_positions: + raise ValidationError( + _("No budget position found that contains:\n" + "- Expense Account: %s\n" + "- For Analytic Account: %s\n" + "- Product: %s") + % (account_id.name, analytic_account.name, rec.product_id.name) + ) + + if len(valid_budget_positions) > 1: + raise ValidationError( + _("Multiple budget positions found for expense account %s with analytic account %s:\n%s\n\n" + "Please contact your administrator to fix the budget configuration.") + % (account_id.name, analytic_account.name, ', '.join(valid_budget_positions.mapped('name'))) + ) + + final_budget_line = budget_lines.filtered( + lambda x: x.general_budget_id.id == valid_budget_positions.id + ) + + if not final_budget_line: + raise ValidationError(_("Budget line not found for the configuration")) + + final_budget_line = final_budget_line[0] + + self.budget_id = final_budget_line.crossovered_budget_id.id + remain = abs(final_budget_line.remain) + amount += rec.price_subtotal + new_balance = remain - amount + + confirmation_lines.append((0, 0, { + 'amount': rec.price_subtotal, + 'analytic_account_id': analytic_account.id, + 'description': rec.product_id.name, + 'budget_line_id': final_budget_line.id, + 'remain': remain, + 'new_balance': new_balance, + 'account_id': account_id.id + })) + + data = { + 'name': self.name, + 'date': self.date_order, + 'beneficiary_id': self.partner_id.id, + 'department_id': self.department_id.id, + 'type': 'purchase.order', + 'ref': self.name, + 'description': self.purpose, + 'total_amount': self.amount_untaxed, + 'lines_ids': confirmation_lines, + 'po_id': self.id + } + self.env['budget.confirmation'].with_context({}).create(data) + self.write({'state': 'waiting'}) diff --git a/odex25_purchase/purchase_budget_fix/security/ir.model.access.csv b/odex25_purchase/purchase_budget_fix/security/ir.model.access.csv new file mode 100644 index 000000000..97dd8b917 --- /dev/null +++ b/odex25_purchase/purchase_budget_fix/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink