From 99626f917b032110fe12b9a0ee86c218c0367be1 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 24 Mar 2025 15:34:56 +0700 Subject: push --- indoteknik_custom/models/mrp_production.py | 169 ++++++++++++++++++++++++- indoteknik_custom/models/stock_move.py | 14 ++ indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/mrp_production.xml | 30 +++++ 4 files changed, 212 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 54d90256..ed05de91 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -1,4 +1,7 @@ -from odoo import fields, models, api, _ +from odoo import models, fields, api, tools, _ +from datetime import datetime, timedelta +import math +import logging from odoo.exceptions import AccessError, UserError, ValidationError @@ -6,5 +9,167 @@ class MrpProduction(models.Model): _inherit = 'mrp.production' desc = fields.Text(string='Description') + status = fields.Selection([('pending', 'Pending'), ('approved', 'Approved'), ('reject', 'Reject')], string='Status', default='pending', tracking=3) + production_purchase_match = fields.One2many('production.purchase.match', 'production_id', string='Purchase Matches', auto_join=True) + + def action_approve(self): + if self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and self.status == 'pending': + self.status = 'approved' + + def action_reject(self): + if self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and self.status == 'pending': + self.status = 'reject' + + def create_po_from_manufacturing(self): + if not self.status == 'approved': + raise UserError('Harus Di Approve oleh Merchandiser') + + if not self.move_raw_ids: + raise UserError('Tidak ada Lines, belum bisa create PO') + # if self.is_po: + # raise UserError('Sudah pernah di create PO') + + vendor_ids = self.env['stock.move'].read_group([ + ('raw_material_production_id', '=', self.id), + ('vendor_id', '!=', False) + ], fields=['vendor_id'], groupby=['vendor_id']) + + po_ids = [] + for vendor in vendor_ids: + result_po = self.create_po_by_vendor(vendor['vendor_id'][0]) + po_ids += result_po + return { + 'name': _('Purchase Order'), + 'view_mode': 'tree,form', + 'res_model': 'purchase.order', + 'target': 'current', + 'type': 'ir.actions.act_window', + 'domain': [('id', 'in', po_ids)], + } + + + def create_po_by_vendor(self, vendor_id): + current_time = datetime.now() + + PRODUCT_PER_PO = 20 + + stock_move = self.env['stock.move'] + + param_header = { + 'partner_id': vendor_id, + # 'partner_ref': self.sale_order_id.name, + 'currency_id': 12, + 'user_id': self.env.user.id, + 'company_id': 1, # indoteknik dotcom gemilang + 'picking_type_id': 28, # indoteknik bandengan receipts + 'date_order': current_time, + # 'sale_order_id': self.sale_order_id.id, + 'note_description': 'from Manufacturing Order' + } + + domain = [ + ('raw_material_production_id', '=', self.id), + ('vendor_id', '=', vendor_id), + ('state', 'in', ['waiting','confirmed','partially_available']) + ] + + products_len = stock_move.search_count(domain) + page = math.ceil(products_len / PRODUCT_PER_PO) + po_ids = [] + # i start from zero (0) + for i in range(page): + new_po = self.env['purchase.order'].create([param_header]) + new_po.name = new_po.name + "/MO/" + str(i + 1) + po_ids.append(new_po.id) + lines = stock_move.search( + domain, + offset=i * PRODUCT_PER_PO, + limit=PRODUCT_PER_PO + ) + tax = [22] + + for line in lines: + product = line.product_id + price, taxes, vendor = self._get_purchase_price(product) + + param_line = { + 'order_id' : new_po.id, + 'product_id': product.id, + 'product_qty': line.product_uom_qty if line.state in ['confirmed', 'waiting'] else line.product_uom_qty - line.forecast_availability, + 'product_uom_qty': line.product_uom_qty if line.state in ['confirmed', 'waiting'] else line.product_uom_qty - line.forecast_availability, + 'name': product.display_name, + 'price_unit': price, + 'taxes_id': [taxes], + } + new_po_line = self.env['purchase.order.line'].create([param_line]) + + self.env['production.purchase.match'].create([{ + 'production_id': self.id, + 'order_id': new_po.id + }]) + # self.is_po = True + + return po_ids + + def _get_purchase_price(self, product_id): + override_vendor = product_id.x_manufacture.override_vendor_id + if override_vendor: + query = [('product_id', '=', product_id.id), + ('vendor_id', '=', override_vendor.id)] + purchase_price = self.env['purchase.pricelist'].search(query, limit=1) + return self._get_valid_purchase_price(purchase_price) + else: + purchase_price = self.env['purchase.pricelist'].search( + [('product_id', '=', product_id.id), + ('is_winner', '=', True)], + limit=1) + + return self._get_valid_purchase_price(purchase_price) + + def _get_valid_purchase_price(self, purchase_price): + current_time = datetime.now() + delta_time = current_time - timedelta(days=365) + # delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S') + + price = 0 + taxes = '' + vendor_id = '' + human_last_update = purchase_price.human_last_update or datetime.min + system_last_update = purchase_price.system_last_update or datetime.min + + if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id + vendor_id = purchase_price.vendor_id.id + if delta_time > human_last_update: + price = 0 + taxes = '' + vendor_id = '' + + if system_last_update > human_last_update: + if purchase_price.taxes_system_id.type_tax_use == 'purchase': + price = purchase_price.system_price + taxes = purchase_price.taxes_system_id.id + vendor_id = purchase_price.vendor_id.id + if delta_time > system_last_update: + price = 0 + taxes = '' + vendor_id = '' + + return price, taxes, vendor_id - \ No newline at end of file + +class ProductionPurchaseMatch(models.Model): + _name = 'production.purchase.match' + _order = 'production_id, id' + + production_id = fields.Many2one('mrp.production', string='Ref', required=True, ondelete='cascade', index=True, copy=False) + order_id = fields.Many2one('purchase.order', string='Purchase Order') + vendor = fields.Char(string='Vendor', compute='_compute_info_po') + total = fields.Float(string='Total', compute='_compute_info_po') + + def _compute_info_po(self): + for match in self: + match.vendor = match.order_id.partner_id.name + match.total = match.order_id.amount_total + diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 6b631713..b5fc782e 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -13,6 +13,20 @@ class StockMove(models.Model): ) qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') barcode = fields.Char(string='Barcode', related='product_id.barcode') + vendor_id = fields.Many2one('res.partner' ,string='Vendor') + + @api.onchange('product_id') + def onchange_product_to_fill_vendor(self): + if self.product_id: + if self.product_id.x_manufacture.override_vendor_id: + self.vendor_id = self.product_id.x_manufacture.override_vendor_id.id + else: + purchase_pricelist = self.env['purchase.pricelist'].search( + [('product_id', '=', product_id.id), + ('is_winner', '=', True)], + limit=1) + if purchase_pricelist: + self.vendor_id = purchase_pricelist.vendor_id.id def _compute_qr_code_variant(self): for rec in self: diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 4d0e51eb..8e9a08f0 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -168,3 +168,4 @@ access_account_payment_register,access.account.payment.register,model_account_pa access_stock_inventory,access.stock.inventory,model_stock_inventory,,1,1,1,1 access_cancel_reason_order,cancel.reason.order,model_cancel_reason_order,,1,1,1,0 access_shipping_option,shipping.option,model_shipping_option,,1,1,1,1 +access_production_purchase_match,access.production.purchase.match,model_production_purchase_match,,1,1,1,1 diff --git a/indoteknik_custom/views/mrp_production.xml b/indoteknik_custom/views/mrp_production.xml index f81d65e8..a28cdff8 100644 --- a/indoteknik_custom/views/mrp_production.xml +++ b/indoteknik_custom/views/mrp_production.xml @@ -5,9 +5,27 @@ mrp.production + + + + + + + + + + + + @@ -21,4 +39,16 @@ + + + production.purchase.match.tree + production.purchase.match + + + + + + + + -- cgit v1.2.3 From db3ff0677f9c6ddd0f04ebcf3e9c780045259f73 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 25 Mar 2025 13:48:16 +0700 Subject: fix get purchase pricelist --- indoteknik_custom/models/mrp_production.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index ed05de91..0e17fda9 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -98,8 +98,8 @@ class MrpProduction(models.Model): 'product_qty': line.product_uom_qty if line.state in ['confirmed', 'waiting'] else line.product_uom_qty - line.forecast_availability, 'product_uom_qty': line.product_uom_qty if line.state in ['confirmed', 'waiting'] else line.product_uom_qty - line.forecast_availability, 'name': product.display_name, - 'price_unit': price, - 'taxes_id': [taxes], + 'price_unit': price if price else 0.0, + 'taxes_id': [taxes] if taxes else [], } new_po_line = self.env['purchase.order.line'].create([param_line]) @@ -113,10 +113,10 @@ class MrpProduction(models.Model): def _get_purchase_price(self, product_id): override_vendor = product_id.x_manufacture.override_vendor_id - if override_vendor: - query = [('product_id', '=', product_id.id), - ('vendor_id', '=', override_vendor.id)] - purchase_price = self.env['purchase.pricelist'].search(query, limit=1) + query = [('product_id', '=', product_id.id), + ('vendor_id', '=', override_vendor.id)] + purchase_price = self.env['purchase.pricelist'].search(query, limit=1) + if purchase_price: return self._get_valid_purchase_price(purchase_price) else: purchase_price = self.env['purchase.pricelist'].search( -- cgit v1.2.3 From e926482af5f2b95ff465445215c77161223ee671 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 27 Mar 2025 14:38:57 +0700 Subject: push --- indoteknik_custom/models/mrp_production.py | 11 ++--------- indoteknik_custom/models/purchase_order.py | 17 +++++++++++++++++ indoteknik_custom/models/purchasing_job.py | 10 ++++++++++ indoteknik_custom/models/purchasing_job_state.py | 3 ++- indoteknik_custom/models/stock_move.py | 4 ++-- indoteknik_custom/views/mrp_production.xml | 9 +-------- indoteknik_custom/views/purchase_order.xml | 1 + indoteknik_custom/views/purchasing_job.xml | 1 + 8 files changed, 36 insertions(+), 20 deletions(-) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 0e17fda9..561f5b3c 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -9,19 +9,11 @@ class MrpProduction(models.Model): _inherit = 'mrp.production' desc = fields.Text(string='Description') - status = fields.Selection([('pending', 'Pending'), ('approved', 'Approved'), ('reject', 'Reject')], string='Status', default='pending', tracking=3) production_purchase_match = fields.One2many('production.purchase.match', 'production_id', string='Purchase Matches', auto_join=True) - def action_approve(self): - if self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and self.status == 'pending': - self.status = 'approved' - - def action_reject(self): - if self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and self.status == 'pending': - self.status = 'reject' def create_po_from_manufacturing(self): - if not self.status == 'approved': + if not self.state == 'confirmed': raise UserError('Harus Di Approve oleh Merchandiser') if not self.move_raw_ids: @@ -63,6 +55,7 @@ class MrpProduction(models.Model): 'company_id': 1, # indoteknik dotcom gemilang 'picking_type_id': 28, # indoteknik bandengan receipts 'date_order': current_time, + 'product_bom_id': self.product_id.id, # 'sale_order_id': self.sale_order_id.id, 'note_description': 'from Manufacturing Order' } diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index d90c4a8a..b107f389 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -74,6 +74,7 @@ class PurchaseOrder(models.Model): date_done_picking = fields.Datetime(string='Date Done Picking', compute='get_date_done') bills_dp_id = fields.Many2one('account.move', string='Bills DP') bills_pelunasan_id = fields.Many2one('account.move', string='Bills Pelunasan') + product_bom_id = fields.Many2one('product.product', string='Product Bom') grand_total = fields.Monetary(string='Grand Total', help='Amount total + amount delivery', compute='_compute_grand_total') total_margin_match = fields.Float(string='Total Margin Match', compute='_compute_total_margin_match') approve_by = fields.Many2one('res.users', string='Approve By') @@ -726,9 +727,25 @@ class PurchaseOrder(models.Model): self.unlink_purchasing_job_state() self._check_qty_plafon_product() + if self.product_bom_id: + self._remove_product_bom() return res + def _remove_product_bom(self): + pj = self.env['v.purchasing.job'].search([ + ('product_id', '=', self.product_bom_id.id) + ]) + + if pj: + pj_state = self.env['purchasing.job.state'].search([ + ('purchasing_job_id', '=', pj.id) + ]) + + if pj_state: + pj_state.note = 'Product BOM Sudah Di PO' + pj_state.date_po = datetime.utcnow() + def check_ppn_mix(self): reference_taxes = self.order_line[0].taxes_id diff --git a/indoteknik_custom/models/purchasing_job.py b/indoteknik_custom/models/purchasing_job.py index 902bc34b..862e72c7 100644 --- a/indoteknik_custom/models/purchasing_job.py +++ b/indoteknik_custom/models/purchasing_job.py @@ -25,6 +25,15 @@ class PurchasingJob(models.Model): ], string='APO?') purchase_representative_id = fields.Many2one('res.users', string="Purchase Representative", readonly=True) note = fields.Char(string="Note Detail") + date_po = fields.Datetime(string='Date PO', copy=False) + + def unlink(self): + # Example: Delete related records from the underlying model + underlying_records = self.env['purchasing.job'].search([ + ('product_id', 'in', self.mapped('product_id').ids) + ]) + underlying_records.unlink() + return super(PurchasingJob, self).unlink() def redirect_to_pjs(self): states = self.env['purchasing.job.state'].search([ @@ -56,6 +65,7 @@ class PurchasingJob(models.Model): pmp.action, max(pjs.status_apo::text) AS status_apo, max(pjs.note::text) AS note, + max(pjs.date_po::text) AS date_po, CASE WHEN pmp.brand IN ('Tekiro', 'RYU', 'Rexco') THEN 27 WHEN sub.vendor_id = 9688 THEN 397 diff --git a/indoteknik_custom/models/purchasing_job_state.py b/indoteknik_custom/models/purchasing_job_state.py index 1838a496..d014edfe 100644 --- a/indoteknik_custom/models/purchasing_job_state.py +++ b/indoteknik_custom/models/purchasing_job_state.py @@ -14,4 +14,5 @@ class PurchasingJobState(models.Model): ('not_apo', 'Belum APO'), ('apo', 'APO') ], string='APO?', copy=False) - note = fields.Char(string="Note Detail") + note = fields.Char(string="Note Detail", copy=False) + date_po = fields.Datetime(string='Date PO', copy=False) diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index b5fc782e..87b1c94e 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -15,8 +15,8 @@ class StockMove(models.Model): barcode = fields.Char(string='Barcode', related='product_id.barcode') vendor_id = fields.Many2one('res.partner' ,string='Vendor') - @api.onchange('product_id') - def onchange_product_to_fill_vendor(self): + @api.constrains('product_id') + def constrains_product_to_fill_vendor(self): if self.product_id: if self.product_id.x_manufacture.override_vendor_id: self.vendor_id = self.product_id.x_manufacture.override_vendor_id.id diff --git a/indoteknik_custom/views/mrp_production.xml b/indoteknik_custom/views/mrp_production.xml index a28cdff8..e639b988 100644 --- a/indoteknik_custom/views/mrp_production.xml +++ b/indoteknik_custom/views/mrp_production.xml @@ -6,17 +6,10 @@ - - - diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 36c0db13..d6ad2408 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -65,6 +65,7 @@ + diff --git a/indoteknik_custom/views/purchasing_job.xml b/indoteknik_custom/views/purchasing_job.xml index 16f1bedd..bb1c7643 100644 --- a/indoteknik_custom/views/purchasing_job.xml +++ b/indoteknik_custom/views/purchasing_job.xml @@ -17,6 +17,7 @@ + -- cgit v1.2.3