This commit is contained in:
esam 2025-11-18 15:49:26 -05:00
parent fb068b1ffc
commit 15f543fd62
8 changed files with 882 additions and 113 deletions

View File

@ -138,9 +138,11 @@ msgid "Certificates of Completion"
msgstr "شهادات الانجاز"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__coc_id
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_partial_wizard__coc_id
msgid "CoC"
msgstr ""
msgstr "شهادة إنجاز"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_order__coc
@ -413,12 +415,6 @@ msgstr ""
msgid "Number of unread messages"
msgstr ""
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/models.py:0
#, python-format
msgid "Only the department manager can approve this CoC."
msgstr ""
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_reject_wizard_coc__origin
msgid "Origin"
@ -622,12 +618,6 @@ msgstr ""
msgid "Website communication history"
msgstr ""
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/models.py:0
#, python-format
msgid "You are not allowed to approve this CoC."
msgstr ""
#. module: odex25_purchase_coc
#: model:ir.model.fields,help:odex25_purchase_coc.field_purchase_coc__vendor_id
msgid "You can find a vendor by its Name, TIN, Email or Internal Reference."
@ -663,11 +653,9 @@ msgid "price"
msgstr ""
#. module: odex25_purchase_coc
#. openerp-web
#: code:addons/odex25_purchase_coc/static/src/xml/tender_templates.xml:0
#, python-format
msgid "product"
msgstr ""
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__product_id
msgid "Product"
msgstr "المنتج"
#. module: odex25_purchase_coc
#. openerp-web
@ -699,13 +687,6 @@ msgid "الكميات الجزئية"
msgstr ""
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odoo/dev_purchase/odex25_purchase/odex25_purchase_coc/models/models.py:0
#, python-format
msgid "You must receive at least one product before confirmation."
msgstr ".يجب استلام منتج واحد على الأقل قبل التأكيد"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc__receiving_meth
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_order_line__receiving_meth
@ -721,4 +702,158 @@ msgstr "كمية"
#. module: odex25_purchase_coc
#: model:ir.model.fields.selection,name:odex25_purchase_coc.selection__purchase_coc__receiving_meth__percentage
msgid "Percentage"
msgstr "بنسبة الإنجاز"
msgstr "بنسبة الإنجاز"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc__work_done_scope
msgid "Executed as per scope?"
msgstr "هل تم تنفيذ الأعمال حسب نطاق العمل؟"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc__work_acceptable
msgid "Are works acceptable?"
msgstr "هل الأعمال المقدمة مقبولة؟"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc__on_time
msgid "Delivered on time?"
msgstr "هل تم تنفيذ الأعمال في الوقت المحدد؟"
#. module: odex25_purchase_coc
#: model:ir.model.fields.selection,name:odex25_purchase_coc.selection__purchase_coc__on_time__yes
#: model:ir.model.fields.selection,name:odex25_purchase_coc.selection__purchase_coc__work_acceptable__yes
#: model:ir.model.fields.selection,name:odex25_purchase_coc.selection__purchase_coc__work_done_scope__yes
msgid "Yes"
msgstr "نعم"
#. module: odex25_purchase_coc
#: model:ir.model.fields.selection,name:odex25_purchase_coc.selection__purchase_coc__on_time__no
#: model:ir.model.fields.selection,name:odex25_purchase_coc.selection__purchase_coc__work_acceptable__no
#: model:ir.model.fields.selection,name:odex25_purchase_coc.selection__purchase_coc__work_done_scope__no
msgid "No"
msgstr "لا"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__po_line_id
msgid "Original PO Line"
msgstr " أمر الشراء الأصلي"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__name
msgid "Description"
msgstr "الوصف"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__product_qty
msgid "Ordered Qty"
msgstr "الكمية"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__qty_ordered
#: model_terms:ir.ui.view,arch_db:odex25_purchase_coc.purchase_coc_form
msgid "Total Ordered"
msgstr "الكمية"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__qty_received_total
#: model_terms:ir.ui.view,arch_db:odex25_purchase_coc.purchase_coc_form
msgid "Previously Received"
msgstr "المستلم سابقاً"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__qty_remaining
#: model_terms:ir.ui.view,arch_db:odex25_purchase_coc.purchase_coc_form
msgid "Remaining"
msgstr "الكمية المتبقية"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__qty_to_receive
#: model_terms:ir.ui.view,arch_db:odex25_purchase_coc.purchase_coc_form
msgid "Qty to Receive"
msgstr "كمية الإستلام"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__price_unit
msgid "Unit Price"
msgstr "سعر الوحدة"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__product_uom
msgid "Unit of Measure"
msgstr "وحدة القياس"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__partner_id
msgid "Partner"
msgstr "الشريك"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__date_planned
msgid "Date "
msgstr "تاريخ الإستلام"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_line__sub_price_total
msgid "Subtotal"
msgstr "الإجمالي الفرعي"
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/new_models.py:0
#, python-format
msgid "You must receive at least one product before confirmation."
msgstr ".يجب استلام منتج واحد على الأقل قبل التأكيد"
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/new_models.py:0
#, python-format
msgid "You are not allowed to approve this CoC."
msgstr "غير مسموح لك بالموافقة على هذا الـ CoC."
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/new_models.py:0
#: code:addons/odex25_purchase_coc/models/new_models.py:0
#, python-format
msgid "Sorry, you are not allowed to confirm this CoC."
msgstr "عذرًا، غير مسموح لك بتأكيد هذا الـ CoC."
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/new_models.py:0
#, python-format
msgid "Received quantity for %s cannot exceed remaining quantity (%.2f)"
msgstr "الكمية المستلمة لـ %s لا يمكن أن تتجاوز الكمية المتبقية (%.2f)"
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/models.py:0
#: code:addons/odex25_purchase_coc/models/new_models.py:0
#, python-format
msgid "Only the department manager can approve this CoC."
msgstr "فقط المدير يمكنه الموافقة على هذا الـ CoC."
#. module: odex25_purchase_coc
#: code:addons/odex25_purchase_coc/models/new_models.py:0
#, python-format
msgid "You must receive at least one product before approval."
msgstr "يجب استلام منتج واحد على الأقل قبل الموافقة."
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_partial_wizard__backorder_lines_data
msgid "Backorder Lines Data"
msgstr "بيانات بنود الطلب المتأخر"
#. module: odex25_purchase_coc
#: model:ir.model.fields,field_description:odex25_purchase_coc.field_purchase_coc_partial_wizard__create_backorder
msgid "Create Backorder?"
msgstr "إنشاء طلب متأخر؟"

View File

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
from . import models
from . import new_models

View File

@ -0,0 +1,558 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from datetime import datetime
from odoo.exceptions import ValidationError
import json
class POCustom(models.Model):
_inherit = 'purchase.order'
need_coc = fields.Boolean(string='Need CoC?')
coc_id = fields.Many2one(comodel_name='purchase.coc', string='CoC Ref')
coc = fields.Boolean(string='CoC Created')
coc_created = fields.Boolean('COC Created')
coc_ids = fields.One2many(comodel_name='purchase.coc', inverse_name='po_id', string='CoCs')
coc_count = fields.Integer(string='Cocs', compute="_compute_coc_count")
is_manual_receipt_user = fields.Boolean(
string='Is Manual Receipt User',
compute='_compute_is_manual_receipt_user',
store=False
)
def _compute_is_manual_receipt_user(self):
"""
"""
for order in self:
order.is_manual_receipt_user = self.env.user.has_group('odex25_purchase_coc.group_coc_manual_receipt')
print("order.is_manual_receipt_user",order.is_manual_receipt_user)
@api.depends('coc_ids')
def _compute_coc_count(self):
for rec in self:
rec.coc_count = self.env['purchase.coc'].search_count([('po_id', '=', rec.id)])
def button_confirm(self):
super_action = super(POCustom, self).button_confirm()
service_products = self.order_line.filtered(lambda line: line.product_id.type == "service")
if service_products:
self.action_create_coc()
return super_action
def action_view_coc(self):
return {
'type': 'ir.actions.act_window',
'name': 'Certificate Of Completions',
'res_model': 'purchase.coc',
'view_mode': 'tree,form',
'target': 'current',
'domain': [('po_id', '=', self.id)],
'context': {'create': False}
}
def action_create_coc(self):
coc = self.env['purchase.coc'].create({
'vendor_id': self.partner_id.id,
'date': datetime.today(),
'po_id': self.id,
'state': 'draft'
})
for po_line in self.order_line.filtered(lambda line: line.product_id.type == 'service'):
self.env['purchase.coc.line'].create({
'coc_id': coc.id,
'po_line_id': po_line.id,
'product_id': po_line.product_id.id,
'product_qty': po_line.product_qty,
'qty_ordered': po_line.product_qty,
'qty_received_total': 0.0,
'qty_remaining': po_line.product_qty,
'price_unit': po_line.price_unit,
'product_uom': po_line.product_uom.id,
'name': po_line.name,
'partner_id':self.partner_id.id,
'date_planned':po_line.date_planned,
'currency_id': po_line.currency_id.id,
'sub_price_total':po_line.price_subtotal,
})
self.coc_id = coc.id
self.coc_created = True
def action_create_invoice(self):
return super().action_create_invoice()
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
qty_history = fields.One2many('purchase.order.line.history', 'po_line_id', string='Qty History')
@api.depends('product_qty', 'qty_received', 'qty_history')
def _compute_qty_remain(self):
for rec in self:
sum_qty_received = sum(rec.qty_history.mapped('qty_received'))
rec.back_qty_remain = rec.product_qty - sum_qty_received
class PurchaseOrderLineHistory(models.Model):
_name = 'purchase.order.line.history'
_description = 'Purchase Order Line History'
po_line_id = fields.Many2one('purchase.order.line', string='PO Line', required=True, ondelete='cascade')
coc_id = fields.Many2one('purchase.coc', string='CoC Reference')
qty_received = fields.Float(string='Qty Received', required=True)
date = fields.Datetime(string='Date', default=fields.Datetime.now)
class PurchaseCoCLine(models.Model):
_name = 'purchase.coc.line'
_description = 'Purchase CoC Line'
coc_id = fields.Many2one('purchase.coc', string='CoC', required=True, ondelete='cascade')
po_line_id = fields.Many2one('purchase.order.line', string='Original PO Line', required=True)
product_id = fields.Many2one('product.product', string='Product', required=True)
name = fields.Text(string='Description', required=True)
product_qty = fields.Float(string='Ordered Qty', required=True)
qty_ordered = fields.Float(string='Total Ordered', help="الكمية الإجمالية المطلوبة من أمر الشراء")
qty_received_total = fields.Float(string='Previously Received', help="المستلم في شهادات سابقة")
qty_remaining = fields.Float(string='Remaining', compute='_compute_qty_remaining', store=True)
qty_to_receive = fields.Float(string='Qty to Receive', help="الكمية المراد استلامها في هذه الشهادة")
qty_percentage = fields.Float(string='%')
price_unit = fields.Float(string='Unit Price')
product_uom = fields.Many2one('uom.uom', string='Unit of Measure')
partner_id = fields.Many2one('res.partner', string='Partner')
date_planned = fields.Datetime(string='Date ')
receiving_meth = fields.Selection(
related='coc_id.receiving_meth',
string='Receiving Method',
store=False
)
currency_id = fields.Many2one(
'res.currency',
string="Currency",
required=True,
)
sub_price_total = fields.Monetary(
string="Subtotal",
currency_field='currency_id',
)
show_percentage = fields.Boolean(
string="Show Percentage",
compute="_compute_show_percentage",
store=False
)
@api.depends('coc_id.receiving_meth')
def _compute_show_percentage(self):
for line in self:
line.show_percentage = (line.receiving_meth == 'percentage')
@api.depends('product_qty', 'qty_received_total')
def _compute_qty_remaining(self):
for line in self:
line.qty_remaining = line.qty_ordered - line.qty_received_total
@api.onchange('qty_percentage')
def _onchange_qty_percentage(self):
if self.qty_percentage and self.qty_remaining:
self.qty_to_receive = (self.qty_percentage / 100) * self.qty_remaining
@api.onchange('qty_to_receive')
def _onchange_qty_to_receive(self):
if self.qty_to_receive and self.qty_remaining:
self.qty_percentage = (self.qty_to_receive / self.qty_remaining) * 100
# @api.constrains('qty_to_receive', 'qty_remaining')
# def _check_qty_to_receive(self):
# for line in self:
# if line.qty_to_receive > line.qty_remaining:
# raise ValidationError(
# _('Quantity to receive (%.2f) cannot exceed remaining quantity (%.2f) for product %s') %
# (line.qty_to_receive, line.qty_remaining, line.product_id.display_name)
# )
class AccountInvoiceCustom(models.Model):
_inherit = 'account.move'
def action_post(self):
if self.move_type == 'in_invoice':
context = self.env.context
po = self.env['purchase.order'].search([('id', '=', context.get('active_id', False))])
if po and po.coc_ids.filtered(lambda coc: coc.coc_stage == 'befor_bill_valid' and coc.state != 'approve'):
raise ValidationError(_("Sorry You cannot Validate Bill until CoC Created and Approved."))
return super(AccountInvoiceCustom, self).action_post()
def action_confirm(self):
if self.move_type == 'in_invoice':
context = self.env.context
po = self.env['purchase.order'].search([('id', '=', context.get('active_id', False))])
if po and po.coc_ids.filtered(lambda coc: coc.coc_stage == 'befor_bill_valid' and coc.state != 'approve'):
raise ValidationError(_("Sorry You cannot Validate Bill until CoC Created and Approved."))
return super(AccountInvoiceCustom, self).action_confirm()
def action_register_payment(self):
if self.move_type == 'in_invoice':
context = self.env.context
po = self.env['purchase.order'].search([('id', '=', context.get('active_id', False))])
if po and po.coc_ids.filtered(lambda coc: coc.coc_stage == 'before_payment' and coc.state != 'approve'):
raise ValidationError(_("Sorry You cannot Pay For This Vendor until CoC Created and Approved."))
return super(AccountInvoiceCustom, self).action_register_payment()
class PurchaseCoC(models.Model):
_name = 'purchase.coc'
_description = 'Purchase CoC'
_inherit = ['mail.thread']
parent_coc_id = fields.Many2one('purchase.coc', string='Parent CoC', copy=False)
name = fields.Char(string='Name', copy=False)
coc_stage = fields.Selection(
string='CoC Stage',
selection=[
('before_bill', 'Before Bill'),
('befor_bill_valid', 'Before Bill Validation'),
('before_payment', 'Before Payment')
],
default='before_bill',
tracking=True,
copy=False
)
po_id = fields.Many2one('purchase.order', string='Po Ref.', tracking=True, required=True)
vendor_id = fields.Many2one('res.partner', string='Vendor', related="po_id.partner_id", tracking=True)
date = fields.Date(string='Date', tracking=True)
note = fields.Text(string='Note', tracking=True)
state = fields.Selection(
string='State',
selection=[
('draft', 'Applicant'),
('manager', 'Direct Manager'),
('approve', 'Approve'),
('cancel', 'Reject'),
],
tracking=True,
default='draft',
copy=False
)
receiving_meth = fields.Selection(
string='Receiving Method',
selection=[
('quantity', 'Quantity'),
('percentage', 'Percentage'),
],
default='quantity',
copy=False
)
is_backorder = fields.Boolean(string='Is Backorder', default=False, copy=False)
coc_line_ids = fields.One2many('purchase.coc.line', 'coc_id', string='CoC Lines')
reject_reason = fields.Char(string='Reject Reason', tracking=True)
tender_name = fields.Char('Tender Name')
attach_no = fields.Integer(string="Documents Count", compute='_compute_attach_no')
company_id = fields.Many2one('res.company', default=lambda self: self.env.user.sudo().company_id)
is_qty_deducted = fields.Boolean(string="Quantity Deducted", default=False, copy=False)
work_done_scope = fields.Selection(
[('yes', 'Yes'), ('no', 'No')],
string='Executed as per scope?',
)
work_done_scope_reason = fields.Char(
string='Reason',
)
work_acceptable = fields.Selection(
[('yes', 'Yes'), ('no', 'No')],
string='Are works acceptable?',
)
work_acceptable_reason = fields.Char(
string='Reason',
)
on_time = fields.Selection(
[('yes', 'Yes'), ('no', 'No')],
string='Delivered on time?',
)
delay_days = fields.Integer(
string='Delay Days',
)
delay_reason = fields.Char(
string='Delay Reason',
)
@api.onchange('work_done_scope')
def _onchange_work_done_scope(self):
if self.work_done_scope == 'yes':
self.work_done_scope_reason = False
@api.onchange('work_acceptable')
def _onchange_work_acceptable(self):
if self.work_acceptable == 'yes':
self.work_acceptable_reason = False
@api.onchange('on_time')
def _onchange_on_time(self):
if self.on_time == 'yes':
self.delay_days = False
self.delay_reason = False
@api.depends('message_attachment_count')
def _compute_attach_no(self):
for rec in self:
rec.attach_no = rec.message_attachment_count
def get_attachments(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'name': 'Attachments',
'res_model': 'ir.attachment',
'view_mode': 'kanban,tree,form',
'domain': [('res_model', '=', self._name), ('res_id', '=', self.id)],
'context': {'default_res_model': self._name, 'default_res_id': self.id},
'target': 'current',
}
@api.model
def create(self, vals):
res = super(PurchaseCoC, self).create(vals)
res.name = self.env['ir.sequence'].next_by_code(self._name) or 'New'
return res
def action_manager_reject(self):
request = self.po_id.request_id
user = self.env.user
if request:
employee = request.employee_id.sudo()
if employee.parent_id.user_id.id != user.id:
raise ValidationError(_('Sorry, you are not allowed to confirm this CoC.'))
else:
department_manager = self.po_id.department_id.manager_id.sudo()
if department_manager.user_id.id != user.id:
raise ValidationError(_('Sorry, you are not allowed to confirm this CoC.'))
if self.env.context['lang'] in ['ar_SY', 'ar_001']:
action_name = 'حدد سبب الرفض'
else:
action_name = 'Specify Reject Reason'
return {
'type': 'ir.actions.act_window',
'name': action_name,
'res_model': 'reject.wizard',
'view_mode': 'form',
'target': 'new',
'context': {'default_origin': self.id, 'default_origin_name': self._name}
}
def action_confirm(self):
self.ensure_one()
user = self.env.user
for rec in self:
request = rec.po_id.request_id
if request:
if request.employee_id.parent_id.user_id.id != user.id:
raise ValidationError(_('Sorry You are not allowed to Confirm this CoC.'))
else:
pass
if not any(line.qty_to_receive > 0 for line in rec.coc_line_ids):
raise ValidationError(_('You must receive at least one product before confirmation.'))
for line in rec.coc_line_ids:
if line.qty_to_receive > line.qty_remaining:
raise ValidationError(
_('Received quantity for %s cannot exceed remaining quantity (%.2f)') %
(line.product_id.display_name, line.qty_remaining)
)
manager = None
manager_name = None
if request and request.employee_id and request.employee_id.coach_id:
coach = request.employee_id.sudo().coach_id
manager = coach.work_email
manager_name = coach.name
elif not request and rec.po_id.user_id.id == user.id and user.employee_id and user.employee_id.coach_id:
coach = user.employee_id.sudo().coach_id
manager = coach.work_email
manager_name = coach.name
if manager:
template = self.env.ref('odex25_purchase_coc.email_template_completion_certificate_confirmation')
if template:
template.with_context(manager_name=manager_name).send_mail(
rec.id,
force_send=True,
email_values={'email_to': manager},
)
rec.write({'state': 'manager'})
def action_approve(self):
self.ensure_one()
"""Validate that the current user has permission to approve this CoC."""
user = self.env.user
po = self.po_id.sudo()
request = po.request_id
if not any(line.qty_to_receive > 0 for line in self.coc_line_ids):
raise ValidationError(_('You must receive at least one product before approval.'))
backorder_lines_data = []
if not self.is_qty_deducted:
for line in self.coc_line_ids:
self.env['purchase.order.line.history'].create({
'po_line_id': line.po_line_id.id,
'coc_id': self.id,
'qty_received': line.qty_to_receive,
'date': fields.Datetime.now()
})
new_qty_received = line.po_line_id.qty_received + line.qty_to_receive
line.po_line_id.write({'qty_received': new_qty_received})
remaining_after = line.qty_remaining - line.qty_to_receive
if remaining_after > 0.01:
backorder_lines_data.append({
'po_line_id': line.po_line_id.id,
'product_id': line.product_id.id,
'name': line.name,
'qty_ordered': line.qty_ordered,
'product_qty': remaining_after,
'qty_received_total': line.qty_received_total + line.qty_to_receive,
'price_unit': line.price_unit,
'product_uom': line.product_uom.id,
'partner_id': line.partner_id.id,
'date_planned': line.date_planned,
'currency_id': line.currency_id.id,
'sub_price_total': line.sub_price_total,
})
self.write({'is_qty_deducted': True})
else:
for line in self.coc_line_ids:
remaining_after = line.qty_remaining - line.qty_to_receive
if remaining_after > 0.01:
backorder_lines_data.append({
'po_line_id': line.po_line_id.id,
'product_id': line.product_id.id,
'name': line.name,
'qty_ordered': line.qty_ordered,
'product_qty': remaining_after,
'qty_received_total': line.qty_received_total + line.qty_to_receive,
'price_unit': line.price_unit,
'product_uom': line.product_uom.id,
'partner_id': line.partner_id.id,
'date_planned': line.date_planned,
'currency_id': line.currency_id.id,
'sub_price_total': line.sub_price_total,
})
if backorder_lines_data:
return {
'name': _('Partial Receipt Confirmation'),
'type': 'ir.actions.act_window',
'res_model': 'purchase.coc.partial.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_coc_id': self.id,
'default_backorder_lines_data': backorder_lines_data,
}
}
else:
self.write({'state': 'approve'})
def _create_backorder_coc(self, backorder_lines_data):
backorder_coc = self.create({
'po_id': self.po_id.id,
'parent_coc_id': self.id,
'is_backorder': True,
'date': fields.Date.today(),
'coc_stage': self.coc_stage,
'receiving_meth': self.receiving_meth,
})
for line_data in backorder_lines_data:
self.env['purchase.coc.line'].create({
'coc_id': backorder_coc.id,
'po_line_id': line_data['po_line_id'],
'product_id': line_data['product_id'],
'name': line_data['name'],
'qty_ordered': line_data['qty_ordered'],
'product_qty': line_data['product_qty'],
'qty_received_total': line_data['qty_received_total'],
'qty_remaining': line_data['product_qty'],
'price_unit': line_data['price_unit'],
'product_uom': line_data['product_uom'],
'partner_id': line_data['partner_id'],
'date_planned': line_data['date_planned'],
'currency_id': line_data['currency_id'],
'sub_price_total': line_data['sub_price_total'],
})
return backorder_coc
def action_cancel(self):
user = self.env.user
po = self.po_id.sudo()
request = po.request_id
if request:
coach_user = request.employee_id.sudo().coach_id.user_id
if coach_user.id != user.id:
raise ValidationError(_('Only the department manager can approve this CoC.'))
else:
dept_manager = self.po_id.department_id.manager_id.sudo()
coach_user = dept_manager.coach_id.user_id
if coach_user.id != user.id:
raise ValidationError(_('You are not allowed to approve this CoC.'))
if self.env.context['lang'] in ['ar_SY', 'ar_001']:
action_name = 'حدد سبب الرفض'
else:
action_name = 'Specify Reject Reason'
return {
'type': 'ir.actions.act_window',
'name': action_name,
'res_model': 'reject.wizard',
'view_mode': 'form',
'target': 'new',
'context': {'default_origin': self.id, 'default_origin_name': self._name}
}
def cancel(self):
self.write({
'state': 'cancel',
'reject_reason': self.env.context.get('reject_reason')
})
self.po_id.message_post(
body=_('CoC %s Rejected By %s. Reject Reason: %s') %
(self.name, self.env.user.name, self.reject_reason or 'No reason provided')
)
def action_draft(self):
self.write({'state': 'draft'})
class RejectWizardCoC(models.TransientModel):
_name = 'reject.wizard.coc'
_description = 'Reject Wizard for CoC'
origin = fields.Integer('Origin ID')
reject_reason = fields.Text(string='Reject Reason', required=True)
origin_name = fields.Char('Origin Model')
def action_reject(self):
origin_rec = self.env[self.origin_name].sudo().browse(self.origin)
if not hasattr(origin_rec, 'cancel'):
raise ValidationError(_('This object cannot be rejected'))
return origin_rec.with_context({'reject_reason': self.reject_reason}).cancel()

View File

@ -5,3 +5,4 @@ access_odex25_purchase_coc_odex25_purchase_coc_manager,odex25_purchase_coc.odex2
access_purchase_order_line_history,purchase_order_line_history_name,model_purchase_order_line_history,,1,1,1,1
access_purchase_coc_partial_wizard,access_purchase_coc_partial_wizard,model_purchase_coc_partial_wizard,base.group_user,1,1,1,1
access_purchase_coc_direct_manager,purchase.coc direct manager,model_purchase_coc,group_purchase_coc_direct_manager,1,1,0,0
access_purchase_coc_line_user,purchase.coc.line user,model_purchase_coc_line,base.group_user,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_purchase_order_line_history purchase_order_line_history_name model_purchase_order_line_history 1 1 1 1
6 access_purchase_coc_partial_wizard access_purchase_coc_partial_wizard model_purchase_coc_partial_wizard base.group_user 1 1 1 1
7 access_purchase_coc_direct_manager purchase.coc direct manager model_purchase_coc group_purchase_coc_direct_manager 1 1 0 0
8 access_purchase_coc_line_user purchase.coc.line user model_purchase_coc_line base.group_user 1 1 1 1

View File

@ -24,10 +24,14 @@
<!-- <record id="group_coc_manager_direct" model="res.groups">-->
<!-- <field name="name">Manager Direct Group</field>-->
<!-- <field name="category_id" ref="odex25_purchase_coc.module_category_coc"/>-->
<!-- </record>-->
<record id="group_coc_manager_direct" model="res.groups">
<field name="name">Manager Direct Group</field>
<field name="category_id" ref="odex25_purchase_coc.module_category_coc"/>
</record>
<record id="group_coc_manual_receipt" model="res.groups">
<field name="name">Manual Receipt</field>
<field name="category_id" ref="odex25_purchase_coc.module_category_coc"/>
</record>
<!--this for direct manager-->
<record id="group_purchase_coc_direct_manager" model="res.groups">
@ -69,27 +73,36 @@
<field name="groups" eval="[(4, ref('odex25_purchase_coc.group_coc_user'))]"/>
</record>
<record id="purchase_coc_rule_manager" model="ir.rule">
<field name="name">Purchase COC Manager</field>
<record id="purchase_coc_rule_manager" model="ir.rule">
<field name="name">Purchase COC With Request (CEO or Creator)</field>
<field name="model_id" ref="model_purchase_coc"/>
<field name="domain_force">['|','|',('create_uid', '=', user.id),('po_id.request_id.employee_id.coach_id.user_id', '=', user.id),('po_id.employee_id.coach_id.user_id', '=', user.id)]
<field name="domain_force">
['&amp;',
('po_id.request_id', '!=', False),
'|',
('create_uid', '=', user.id),
('po_id.request_id.chief_executive_officer', '=', user.id)
]
</field>
<field name="groups" eval="[(4, ref('odex25_purchase_coc.group_coc_manager'))]"/>
</record>
<record id="purchase_coc_rule_manager_no_request" model="ir.rule">
<field name="name">Purchase COC Manager (No Request)</field>
<field name="model_id" ref="model_purchase_coc"/>
<field name="domain_force">
[
('po_id.request_id', '=', False),
('po_id.department_id.manager_id.coach_id.user_id', '=', user.id)
]
</field>
<field name="groups" eval="[(4, ref('odex25_purchase_coc.group_coc_manager'))]"/>
</record>
<record id="purchase_coc_rule_manager_no_request" model="ir.rule">
<field name="name">Purchase COC No Request (CEO or Creator)</field>
<field name="model_id" ref="model_purchase_coc"/>
<field name="domain_force">
['&amp;',
('po_id.request_id', '=', False),
'|',
('create_uid', '=', user.id),
('po_id.chief_executive_officer', '=', user.id)
]
</field>
<field name="groups" eval="[(4, ref('odex25_purchase_coc.group_coc_manager'))]"/>
</record>

View File

@ -1,6 +1,6 @@
<odoo>
<data>
<!-- purchase.order inherit form view -->
<!-- purchase.order inherit form view -->
<record id="view_id" model="ir.ui.view">
<field name="name">purchase.order.inherit.view.form</field>
<field name="model">purchase.order</field>
@ -17,7 +17,20 @@
<!-- <field name="need_coc"/> -->
<!-- <field name="coc_id" readonly="1"/> -->
<field name="coc_created" invisible="1"/>
<field name="is_manual_receipt_user" invisible="1"/>
</xpath>
<xpath expr="//field[@name='order_line']//field[@name='qty_received']" position="attributes">
<attribute name="attrs">
{
'column_invisible': [('parent.state', 'not in', ('purchase', 'done'))],
'readonly': ['|',
('qty_received_method', '!=', 'manual'),
('parent.is_manual_receipt_user', '=', False)
]
}
</attribute>
</xpath>
</field>
</record>
@ -53,8 +66,8 @@
<field name="state" widget="statusbar"/>
</header>
<sheet>
<group>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="get_attachments"
type="object"
@ -63,60 +76,105 @@
<field name="attach_no" widget="statinfo" string="Documents"/>
</button>
</div>
</group>
<group col="4" colspan="2">
<field name="po_id" readonly="1"/>
<field name="vendor_id" readonly="1"/>
<field name="date" readonly="1"/>
<field name="coc_stage"/>
<!-- <field name="tender_name"/>-->
<field name="receiving_meth"/>
<field name="parent_coc_id" readonly="1"/>
<field name="qty_received_fake_coc" readonly="1" invisible="1"
attrs="{'invisible': [('state', '!=', 'approve')]}" />
<field name="is_return" readonly="1" invisible="1"/>
</group>
<group col="2">
<group>
<field name="po_id" readonly="1"/>
<field name="date" readonly="1"/>
<field name="tender_name"/>
<field name="parent_coc_id" readonly="1"/>
</group>
<group>
<field name="vendor_id" readonly="1"/>
<field name="coc_stage" attrs="{'readonly': [('state', '=', 'approve')]}"/>
<field name="receiving_meth" attrs="{'readonly': [('state', '=', 'approve')]}"/>
<field name="work_done_scope"
widget="radio"
options="{'horizontal': true}"
attrs="{'readonly': [('state', '=', 'approve')]}"/>
<field name="work_done_scope_reason"
attrs="{
'readonly': [('state', '=', 'approve')],
'invisible': [('work_done_scope', '!=', 'no')],
'required': [('work_done_scope', '=', 'no')]
}"/>
<field name="work_acceptable"
widget="radio"
options="{'horizontal': true}"
attrs="{'readonly': [('state', '=', 'approve')]}"/>
<field name="work_acceptable_reason"
attrs="{
'readonly': [('state', '=', 'approve')],
'invisible': [('work_acceptable', '!=', 'no')],
'required': [('work_acceptable', '=', 'no')]
}"/>
<field name="on_time"
widget="radio"
options="{'horizontal': true}"
attrs="{'readonly': [('state', '=', 'approve')]}"/>
<field name="delay_days"
attrs="{
'readonly': [('state', '=', 'approve')],
'invisible': [('on_time', '!=', 'no')],
'required': [('on_time', '=', 'no')]
}"/>
<field name="delay_reason"
attrs="{
'readonly': [('state', '=', 'approve')],
'invisible': [('on_time', '!=', 'no')],
'required': [('on_time', '=', 'no')]
}"/>
</group>
</group>
<notebook>
<page string="Products" name="products">
<field name="po_line_ids" readonly="0" attrs="{'invisible': [('is_return', '=', True)]}">
<tree editable="bottom">
<field name="order_id" readonly="1"/>
<field name="name" readonly="1"/>
<field name="partner_id" readonly="1"/>
<field name="product_id" readonly="1"/>
<field name="price_unit" readonly="1"/>
<field name="product_qty" readonly="1"/>
<field name="show_percentage" invisible="1"/>
<field name="qty_percentage" attrs="{'invisible': [('show_percentage', '=', False)]}"/>
<field name="qty_received_fake"/>
<field name="qty_received" readonly="1" invisible="1"/>
<field name="back_qty_remain" readonly="1"/>
<field name="product_uom" readonly="1"/>
<field name="price_subtotal" readonly="1"/>
<field name="date_planned" readonly="1"/>
</tree>
</field >
<field name="po_return_line_ids" readonly="0" attrs="{'invisible': [('is_return', '=', False)]}">
<tree editable="bottom">
<field name="order_id" readonly="1"/>
<field name="name" readonly="1"/>
<field name="partner_id" readonly="1"/>
<field name="product_id" readonly="1"/>
<field name="price_unit" readonly="1"/>
<field name="show_percentage" invisible="1"/>
<field name="qty_percentage" attrs="{'invisible': [('show_percentage', '=', False)]}"/>
<page string="Products" name="products">
<field name="coc_line_ids" attrs="{'readonly': [('state', '=', 'approve')]}">
<tree editable="bottom" create="false" delete="false"
decoration-success="qty_to_receive > 0"
decoration-muted="qty_remaining == 0">
<field name="product_id" readonly="1" width="20"/>
<field name="name" readonly="1" width="20"/>
<field name="qty_ordered" readonly="1" width="20" string="Total Ordered"/>
<field name="product_uom" width="20" readonly="1"/>
<field name="qty_received_total" readonly="1" width="20" string="Previously Received"/>
<!-- <field name="product_qty" readonly="1" width="20" string="Current Order Qty"/>-->
<field name="qty_remaining" readonly="1" width="20" string="Remaining"/>
<field name="qty_received_fake"/>
<field name="qty_received" readonly="1" invisible="1"/>
<field name="back_qty_remain" readonly="1"/>
<field name="product_uom" readonly="1"/>
<field name="price_subtotal" readonly="1"/>
<field name="date_planned" readonly="1"/>
</tree>
</field >
</page>
<field name="show_percentage" invisible="1"/>
<field name="qty_percentage" width="40"
attrs="{'column_invisible': [('parent.receiving_meth', '!=', 'percentage')],
'readonly': [('qty_remaining', '=', 0)]}"
string="%"/>
<field name="qty_to_receive" width="20"
string="Qty to Receive"
attrs="{'column_invisible': [('parent.receiving_meth', '=', 'percentage')],
'readonly': [('qty_remaining', '=', 0)]}"
/>
<field name="date_planned" width="20" attrs="{'readonly': [('parent.state', '=', 'approve')]}" />
<field name="price_unit" width="20" readonly="1"/>
<field name="sub_price_total" width="20" readonly="1"/>
<field name="po_line_id" invisible="1"/>
<field name="receiving_meth" invisible="1"/>
</tree>
</field>
</page>
<page string="Note" name="note">
<field name="note"/>
</page>

View File

@ -2,26 +2,30 @@ from odoo import models, fields, api, _
from datetime import datetime
from odoo.exceptions import ValidationError
import json
class PurchaseCocPartialWizard(models.TransientModel):
import ast
class PurchaseCoCPartialWizard(models.TransientModel):
_name = 'purchase.coc.partial.wizard'
_description = 'Partial Quantities Wizard for CoC'
confirm_create = fields.Boolean(string="Do you want to create a new CoC?", default=True)
coc_id = fields.Many2one('purchase.coc', string="CoC")
backorder_line_ids = fields.Many2many('purchase.order.line', string="Backorder Lines")
_description = 'CoC Partial Receipt Wizard'
coc_id = fields.Many2one('purchase.coc', string='CoC', required=True)
backorder_lines_data = fields.Text(string='Backorder Lines Data')
create_backorder = fields.Boolean(string='Create Backorder?', default=True)
def action_confirm(self):
print("action_confirm")
self.ensure_one()
coc = self.coc_id
backorder = coc._create_backorder(self.backorder_line_ids.ids)
coc.write({'return_id': backorder.id})
return coc._get_backorder_action(backorder)
if self.create_backorder and self.backorder_lines_data:
lines_data = ast.literal_eval(self.backorder_lines_data)
backorder = self.coc_id._create_backorder_coc(lines_data)
self.coc_id.write({'state': 'approve'})
return {'type': 'ir.actions.act_window_close'}
def action_cancel(self):
self.ensure_one()
self.coc_id.write({'state': 'approve'})
return {'type': 'ir.actions.act_window_close'}

View File

@ -11,7 +11,7 @@ You have processed less product than the initial order.
<footer>
<button name="action_confirm" string="Create Backorder" type="object" class="btn-primary"/>
<button string="No Backorder" class="btn-secondary" special="cancel"/>
<button name="action_cancel" string="No Backorder" type="object" class="btn-secondary"/>
</footer>
</form>
</field>