summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-03-27 14:39:56 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-03-27 14:39:56 +0700
commit1e3da858c3d924a8fc61c83115ffce654198b10b (patch)
tree626df0a75225be2a8095fa1a564f88c1e5b17e3f
parenta9eed02ec4f14c368fc98accd25ae8acdc67323b (diff)
parente926482af5f2b95ff465445215c77161223ee671 (diff)
Merge branch 'dev/po-mo' into odoo-backup
# Conflicts: # indoteknik_custom/models/mrp_production.py
-rw-r--r--indoteknik_custom/models/mrp_production.py164
-rwxr-xr-xindoteknik_custom/models/purchase_order.py17
-rw-r--r--indoteknik_custom/models/purchasing_job.py10
-rw-r--r--indoteknik_custom/models/purchasing_job_state.py3
-rw-r--r--indoteknik_custom/models/stock_move.py14
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv1
-rw-r--r--indoteknik_custom/views/mrp_production.xml23
-rwxr-xr-xindoteknik_custom/views/purchase_order.xml1
-rw-r--r--indoteknik_custom/views/purchasing_job.xml1
9 files changed, 231 insertions, 3 deletions
diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py
index 0bf98702..1813dbeb 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
@@ -7,6 +10,7 @@ class MrpProduction(models.Model):
desc = fields.Text(string='Description')
sale_order = fields.Many2one('sale.order', string='Sale Order', required=True, copy=False)
+ production_purchase_match = fields.One2many('production.purchase.match', 'production_id', string='Purchase Matches', auto_join=True)
def action_confirm(self):
"""Override action_confirm untuk mengirim pesan ke Sale Order jika state berubah menjadi 'confirmed'."""
@@ -20,4 +24,160 @@ class MrpProduction(models.Model):
message = _("Manufacturing order telah dibuat dengan nomor %s") % (record.name)
record.sale_order.message_post(body=message)
- return result \ No newline at end of file
+ return result
+
+
+ def create_po_from_manufacturing(self):
+ if not self.state == 'confirmed':
+ 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,
+ 'product_bom_id': self.product_id.id,
+ # '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 if price else 0.0,
+ 'taxes_id': [taxes] if taxes else [],
+ }
+ 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
+ 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(
+ [('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
+
+
+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/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 6b631713..87b1c94e 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.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
+ 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 20b1c5b7..ab377fd3 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -169,3 +169,4 @@ 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_reject_reason_commision,reject.reason.commision,model_reject_reason_commision,,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 95f419f6..ffbc8659 100644
--- a/indoteknik_custom/views/mrp_production.xml
+++ b/indoteknik_custom/views/mrp_production.xml
@@ -5,10 +5,21 @@
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_form_view" />
<field name="arch" type="xml">
+ <button name="button_mark_done" position="after">
+ <button name="create_po_from_manufacturing" type="object" string="Create PO" class="oe_highlight" attrs="{'invisible': [('state', '!=', 'confirmed')]}"/>
+ </button>
<field name="bom_id" position="after">
<field name="desc"/>
<field name="sale_order"/>
</field>
+ <xpath expr="//form/sheet/notebook/page/field[@name='move_raw_ids']/tree/field[@name='product_uom_qty']" position="before">
+ <field name="vendor_id"/>
+ </xpath>
+ <xpath expr="//form/sheet/notebook/page[@name='miscellaneous']" position="after">
+ <page string="Purchase Match" name="purchase_order_lines_indent">
+ <field name="production_purchase_match"/>
+ </page>
+ </xpath>
</field>
</record>
@@ -23,4 +34,16 @@
</field>
</field>
</record>
+
+ <record id="production_purchase_match_tree" model="ir.ui.view">
+ <field name="name">production.purchase.match.tree</field>
+ <field name="model">production.purchase.match</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="order_id" readonly="1"/>
+ <field name="vendor" readonly="1"/>
+ <field name="total" readonly="1"/>
+ </tree>
+ </field>
+ </record>
</odoo>
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 @@
<field name="payment_term_id"/>
<field name="total_cost_service" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/>
<field name="total_delivery_amt" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/>
+ <field name="product_bom_id"/>
</field>
<field name="amount_total" position="after">
<field name="total_margin"/>
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 @@
<field name="status_apo" invisible="1"/>
<field name="action"/>
<field name="note"/>
+ <field name="date_po"/>
</tree>
</field>
</record>