summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-03-24 15:34:56 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-03-24 15:34:56 +0700
commit99626f917b032110fe12b9a0ee86c218c0367be1 (patch)
treed6437487b84d22484556a2961c74d74085b53dd3
parentcc1759574f76b084a1ce44e1acf01ed20dcdd729 (diff)
push
-rw-r--r--indoteknik_custom/models/mrp_production.py169
-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.xml30
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 @@
<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="action_approve" type="object" string="Approve" attrs="{'invisible': [('status', '!=', 'pending')]}"/>
+ </button>
+ <button name="button_mark_done" position="after">
+ <button name="action_reject" type="object" string="Reject" attrs="{'invisible': [('status', '!=', 'pending')]}"/>
+ </button>
+ <button name="button_mark_done" position="after">
+ <button name="create_po_from_manufacturing" type="object" string="Create PO" class="oe_highlight" attrs="{'invisible': [('status', '!=', 'approved')]}"/>
+ </button>
<field name="bom_id" position="after">
<field name="desc"/>
+ <field name="status"/>
</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>
@@ -21,4 +39,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>