diff --git a/odex25_purchase/odex25_annual_purchase/models/addendum.py b/odex25_purchase/odex25_annual_purchase/models/addendum.py
index 4624030c3..58834617e 100644
--- a/odex25_purchase/odex25_annual_purchase/models/addendum.py
+++ b/odex25_purchase/odex25_annual_purchase/models/addendum.py
@@ -9,8 +9,10 @@ class AnnualAddendum(models.Model):
_order = "create_date desc"
name = fields.Char(string="Reference", default=lambda self: self.env['ir.sequence'].next_by_code('odx.annual.addendum'), readonly=True, copy=False)
- agreement_id = fields.Many2one('purchase.requisition', string="Agreement", required=True, domain=[('type_id.code','=','blanket_order')])
- vendor_id = fields.Many2one(related='agreement_id.vendor_id', store=True, readonly=True)
+ agreement_id = fields.Many2one('purchase.requisition', string="Agreement", required=True)
+ annual_request_id = fields.Many2one('odx.annual.request', string="Annual Request", index=True, ondelete='cascade')
+ vendor_id = fields.Many2one(related='annual_request_id.vendor_id', store=True, readonly=True)
+
department_id = fields.Many2one('hr.department', string="Department")
purpose = fields.Char(string="Purpose")
note = fields.Text(string="Notes")
@@ -23,6 +25,9 @@ class AnnualAddendum(models.Model):
('cancel','Cancelled'),
], default='draft', tracking=True)
line_ids = fields.One2many('odx.annual.addendum.line', 'addendum_id', string="Lines")
+ ssd_approve = fields.Boolean(string="SSD Approve", default=False)
+ seo_approve = fields.Boolean(string="SEO Approve", default=False)
+ recommendation = fields.Boolean(string="Recommendation", default=False)
def _check_lines(self):
for rec in self:
@@ -69,6 +74,43 @@ class AnnualAddendumLine(models.Model):
product_id = fields.Many2one('product.product', string="Product", required=True, domain=[('purchase_ok','=',True)])
description = fields.Char(string="Description")
quantity = fields.Float(string="Quantity", default=1.0)
- uom_id = fields.Many2one('uom.uom', string="UoM", related='product_id.uom_po_id', readonly=False)
- price_unit = fields.Monetary(string="Unit Price", currency_field='currency_id', groups="purchase_requisition_custom.committe_member")
- currency_id = fields.Many2one('res.currency', related='addendum_id.agreement_id.company_id.currency_id', readonly=True, store=True)
\ No newline at end of file
+ uom_id = fields.Many2one('uom.uom', string="UoM", related='product_id.uom_po_id')
+ price_unit = fields.Monetary(string="Unit Price", currency_field='currency_id')
+ currency_id = fields.Many2one(
+ 'res.currency',
+ related='addendum_id.agreement_id.company_id.currency_id',
+ readonly=True, store=True
+ )
+ addendum_company_id = fields.Many2one(
+ 'res.company',
+ related='addendum_id.agreement_id.company_id',
+ readonly=True, store=True
+ )
+ taxes_id = fields.Many2many(
+ 'account.tax', string="Taxes",
+ domain="[('type_tax_use','=','purchase'), ('company_id','=', addendum_company_id)]"
+ )
+ price_subtotal = fields.Monetary(
+ string="Subtotal", currency_field='currency_id',
+ compute='_compute_amounts', store=True, readonly=True
+ )
+
+ @api.depends('quantity', 'price_unit', 'taxes_id', 'currency_id', 'product_id')
+ def _compute_amounts(self):
+ for rec in self:
+ qty = rec.quantity or 0.0
+ price_unit = rec.price_unit or 0.0
+ currency = rec.currency_id
+
+ taxes = rec.taxes_id
+ if rec.addendum_company_id:
+ taxes = taxes.filtered(lambda t: t.company_id == rec.addendum_company_id)
+
+ res = taxes.compute_all(
+ price_unit,
+ currency=currency,
+ quantity=qty,
+ product=rec.product_id,
+ partner=None
+ )
+ rec.price_subtotal = res['total_excluded']
diff --git a/odex25_purchase/odex25_annual_purchase/models/annual_request.py b/odex25_purchase/odex25_annual_purchase/models/annual_request.py
index 8a5679f1c..4059311f6 100644
--- a/odex25_purchase/odex25_annual_purchase/models/annual_request.py
+++ b/odex25_purchase/odex25_annual_purchase/models/annual_request.py
@@ -35,8 +35,8 @@ class AnnualPurchaseRequest(models.Model):
note = fields.Text(string="Notes")
state = fields.Selection(selection=STATES, default='draft', tracking=True)
line_ids = fields.One2many('odx.annual.request.line', 'request_id', string="Products")
- agreement_id = fields.Many2one('purchase.requisition', string="Purchase Agreement", readonly=True, tracking=True)
- vendor_id = fields.Many2one('res.partner', string="Selected Vendor", domain=[('supplier_rank','>',0)], tracking=True)
+ agreement_id = fields.Many2one('purchase.requisition', string="Purchase Agreement", readonly=True)
+ vendor_id = fields.Many2one('res.partner', string="Selected Vendor", domain=[('supplier_rank','>',0)])
currency_id = fields.Many2one('res.currency', string="Currency", default=lambda self: self.env.company.currency_id.id)
technical_notes = fields.Html(string="Technical Notes")
financial_notes = fields.Html(string="Financial Notes", groups="purchase_requisition_custom.committe_member")
@@ -46,13 +46,13 @@ class AnnualPurchaseRequest(models.Model):
('rejected','Rejected by Committee'),
('recommended','Recommended'),
('approved','Approved by Committee'),
- ], default='none', tracking=True)
+ ], default='none')
product_category_ids = fields.Many2many('product.category', string='Items Categories',
compute='_compute_product_category_ids',
store=True)
- committee_enabled = fields.Boolean(string="Require Committee Review", default=False, tracking=True)
+ committee_enabled = fields.Boolean(string="Require Committee Review", default=False)
ssd_approve = fields.Boolean(string="SSD Approve", default=False)
seo_approve = fields.Boolean(string="SEO Approve", default=False)
@@ -78,33 +78,71 @@ class AnnualPurchaseRequest(models.Model):
store=False,
readonly=True,
)
+ addendum_count = fields.Integer(string='Addendums', compute='_compute_addendum_count', readonly=True)
+ def _compute_addendum_count(self):
+ Addendum = self.env['odx.annual.addendum']
+ for rec in self:
+ rec.addendum_count = Addendum.search_count([('annual_request_id', '=', rec.id)])
+ def action_open_addendums(self):
+ self.ensure_one()
+ domain = [('annual_request_id', '=', self.id)]
+ return {
+ 'name': _('Addendums'),
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'odx.annual.addendum',
+ 'view_mode': 'tree,form',
+ 'domain': domain,
+ 'context': {
+ 'default_annual_request_id': self.id,
+ 'default_agreement_id': self.agreement_id.id if self.agreement_id else False,
+ 'default_department_id': self.department_id.id if self.department_id else False,
+ 'default_purpose': self.purpose or False,
+ },
+ 'target': 'current',
+ }
def _compute_attach_no(self):
Attachment = self.env['ir.attachment']
for rec in self:
- rec.attach_no = Attachment.search_count([
+ count_self = Attachment.search_count([
('res_model', '=', rec._name),
('res_id', '=', rec.id)
])
+ purchase_orders = self.env['purchase.order'].search([('annual_request_id', '=', rec.id)])
+ count_po = 0
+ if purchase_orders:
+ count_po = Attachment.search_count([
+ ('res_model', '=', 'purchase.order'),
+ ('res_id', 'in', purchase_orders.ids)
+ ])
+
+ rec.attach_no = count_self + count_po
+
def get_attachments(self):
self.ensure_one()
+ Attachment = self.env['ir.attachment']
+
+ purchase_orders = self.env['purchase.order'].search([('annual_request_id', '=', self.id)])
+
+ domain = ['|',
+ '&', ('res_model', '=', self._name), ('res_id', 'in', self.ids),
+ '&', ('res_model', '=', 'purchase.order'), ('res_id', 'in', purchase_orders.ids)
+ ]
+
return {
- 'name': "Documents",
+ 'name': _("Documents"),
'type': 'ir.actions.act_window',
'res_model': 'ir.attachment',
'view_mode': 'kanban,tree,form',
- 'domain': [
- ('res_model', '=', self._name),
- ('res_id', 'in', self.ids)
- ],
+ 'domain': domain,
'context': {
'default_res_model': self._name,
'default_res_id': self.id,
-
},
'target': 'current',
}
+
def copy(self, default=None):
default = dict(default or {})
@@ -219,6 +257,7 @@ class AnnualPurchaseRequest(models.Model):
self._check_lines()
order_line_vals = []
for line in self.line_ids:
+ line.sudo()
order_line_vals.append((0, 0, {
'product_id': line.product_id.id,
'name': line.description or line.product_id.description_purchase or line.product_id.name,
@@ -294,13 +333,21 @@ class AnnualPurchaseRequest(models.Model):
if manager.user_id.id == rec.env.uid :
rec.write({'state': 'procurement'})
else:
- raise Warning(_("Sorry, The Approval For The Direct Manager '%s' Only !")%(rec.employee_id.parent_id.name))
+ raise UserError(_("Sorry, The Approval For The Direct Manager '%s' Only !")%(rec.employee_id.parent_id.name))
else:
rec.write({'state': 'procurement'})
def action_manager_reject(self, reason=False):
- self.message_post(body=_("Rejected by Manager: %s") % (reason or ''))
- self.write({'state': 'rejected'})
+ for rec in self:
+ manager = rec.sudo().employee_id.parent_id
+ if manager:
+ if manager.user_id.id == rec.env.uid :
+ rec.write({'state': 'rejected'})
+ else:
+ raise UserError(_("Sorry, The Approval For The Direct Manager '%s' Only !")%(rec.employee_id.parent_id.name))
+ else:
+ rec.write({'state': 'rejected'})
+
def action_send_to_committee(self):
self.write({'sent_to_commitee': True})
@@ -381,10 +428,10 @@ class AnnualPurchaseRequestLine(models.Model):
_description = "Annual Purchase Request Line"
request_id = fields.Many2one('odx.annual.request', string="Request", required=True, ondelete='cascade')
- product_id = fields.Many2one('product.product', string="Product", required=True, domain=[('purchase_ok','=',True)])
+ product_id = fields.Many2one('product.product', string="Product", required=True)
description = fields.Char(string="Technical Description")
quantity = fields.Float(string="Quantity", default=1.0)
- uom_id = fields.Many2one('uom.uom', string="UoM", related='product_id.uom_po_id', readonly=False)
- price_unit = fields.Monetary(string="Unit Price", currency_field='currency_id', groups="purchase_requisition_custom.committe_member")
+ uom_id = fields.Many2one('uom.uom', string="UoM", related='product_id.uom_po_id')
+ price_unit = fields.Monetary(string="Unit Price", currency_field='currency_id')
currency_id = fields.Many2one('res.currency', related='request_id.currency_id', readonly=True, store=True)
technical_spec = fields.Text(string="Technical Specification")
\ No newline at end of file
diff --git a/odex25_purchase/odex25_annual_purchase/models/purchase_inherit.py b/odex25_purchase/odex25_annual_purchase/models/purchase_inherit.py
index 3e31eda36..431d2cf6c 100644
--- a/odex25_purchase/odex25_annual_purchase/models/purchase_inherit.py
+++ b/odex25_purchase/odex25_annual_purchase/models/purchase_inherit.py
@@ -9,13 +9,16 @@ class PurchaseOrder(models.Model):
department_id = fields.Many2one('hr.department', string="Department")
purpose = fields.Char(string="Purpose")
is_recommended = fields.Boolean(string="Recommended by Committee")
-
- # حقول محسوبة من الاحتياج السنوي
- annual_purchase_commitee = fields.Boolean(
- string='Annual Committee?',
- compute='_compute_annual_committee_fields',
- store=True
+ technical_attachment_id = fields.Many2one(
+ 'ir.attachment',
+ string="Technical Offer Attachment",
)
+ # حقول محسوبة من الاحتياج السنوي
+ # annual_purchase_commitee = fields.Boolean(
+ # string='Annual Committee?',
+ # compute='_compute_annual_committee_fields',
+ # store=True
+ # )
annual_can_committee_vote = fields.Boolean(
compute='_compute_annual_can_committee_vote'
)
@@ -24,7 +27,20 @@ class PurchaseOrder(models.Model):
string='Is Technical Committee',
store=False
)
+ show_send_purchase_manager = fields.Boolean(compute='_compute_show_send_purchase_manager')
+ cancel_reason = fields.Text(string="Cancel Reason")
+ @api.depends('state', 'requisition_state', 'requisition_type_exclusive','requisition_id', 'is_purchase_budget')
+ def _compute_show_send_purchase_manager(self):
+ for record in self:
+ if (
+ (record.state == 'sent' and not record.is_purchase_budget and not record.requisition_id)
+ or
+ (record.state == 'wait' and not record.is_purchase_budget and not record.requisition_id)
+ ):
+ record.show_send_purchase_manager = False # يظهر الزر
+ else:
+ record.show_send_purchase_manager = True # يخفي الزر
@api.depends(
'requisition_id', 'requisition_id.state', 'requisition_id.purchase_commitee',
@@ -168,7 +184,30 @@ class PurchaseOrder(models.Model):
'context': {'default_order_id': self.id}
}
+ def _fill_annual_prices_from_po(self):
+ for po in self:
+ annual = po.annual_request_id
+ if not annual:
+ continue
+
+ for rline in annual.line_ids:
+ pol = po.order_line.filtered(lambda l: l.product_id == rline.product_id)[:1]
+ if not pol:
+ continue
+
+ price = pol.price_unit
+
+ if po.currency_id != annual.currency_id:
+ convert_date = po.date_order.date() if po.date_order else fields.Date.context_today(self)
+ price = po.currency_id._convert(
+ price, annual.currency_id, po.company_id, convert_date
+ )
+
+ if pol.product_uom and rline.uom_id and pol.product_uom != rline.uom_id:
+ price = pol.product_uom._compute_price(price, rline.uom_id)
+
+ rline.price_unit = price
def action_sign(self):
if self.annual_request_id and self.annual_request_id.committee_enabled:
@@ -187,6 +226,7 @@ class PurchaseOrder(models.Model):
for order in other_orders:
order.action_unsign()
self.annual_request_id.vendor_id = self.partner_id.id
+ self._fill_annual_prices_from_po()
if self.annual_request_id.ssd_approve:
self.annual_request_id.state = 'ssd'
elif self.annual_request_id.seo_approve:
diff --git a/odex25_purchase/odex25_annual_purchase/views/addendum_views.xml b/odex25_purchase/odex25_annual_purchase/views/addendum_views.xml
index c6b34b270..93f4c7732 100644
--- a/odex25_purchase/odex25_annual_purchase/views/addendum_views.xml
+++ b/odex25_purchase/odex25_annual_purchase/views/addendum_views.xml
@@ -27,16 +27,25 @@
+
+