summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xindoteknik_custom/models/__init__.py2
-rwxr-xr-xindoteknik_custom/models/sale_order.py1
-rw-r--r--indoteknik_custom/models/sales_order_koli.py25
-rw-r--r--indoteknik_custom/models/stock_backorder_confirmation.py50
-rw-r--r--indoteknik_custom/models/stock_picking.py171
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv2
-rwxr-xr-xindoteknik_custom/views/sale_order.xml15
-rw-r--r--indoteknik_custom/views/stock_picking.xml6
8 files changed, 265 insertions, 7 deletions
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index ed9e91da..a5297806 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -140,3 +140,5 @@ from . import va_multi_reject
from . import stock_immediate_transfer
from . import coretax_fatur
from . import barcoding_product
+from . import sales_order_koli
+from . import stock_backorder_confirmation
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 7b2d9bf8..88c32fb6 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -11,6 +11,7 @@ _logger = logging.getLogger(__name__)
class SaleOrder(models.Model):
_inherit = "sale.order"
+ koli_lines = fields.One2many('sales.order.koli', 'sale_order_id', string='Sales Order Koli', auto_join=True)
fulfillment_line_v2 = fields.One2many('sales.order.fulfillment.v2', 'sale_order_id', string='Fullfillment2')
fullfillment_line = fields.One2many('sales.order.fullfillment', 'sales_order_id', string='Fullfillment')
reject_line = fields.One2many('sales.order.reject', 'sale_order_id', string='Reject Lines')
diff --git a/indoteknik_custom/models/sales_order_koli.py b/indoteknik_custom/models/sales_order_koli.py
new file mode 100644
index 00000000..02e85256
--- /dev/null
+++ b/indoteknik_custom/models/sales_order_koli.py
@@ -0,0 +1,25 @@
+from odoo import fields, models, api, _
+from odoo.exceptions import AccessError, UserError, ValidationError
+from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class SalesOrderKoli(models.Model):
+ _name = 'sales.order.koli'
+ _description = 'Sales Order Koli'
+ _order = 'sale_order_id, id'
+ _rec_name = 'koli_id'
+
+ sale_order_id = fields.Many2one(
+ 'sale.order',
+ string='Sale Order Reference',
+ required=True,
+ ondelete='cascade',
+ index=True,
+ copy=False,
+ )
+ koli_id = fields.Many2one('check.koli', string='Koli')
+ picking_id = fields.Many2one('stock.picking', string='Picking')
+
diff --git a/indoteknik_custom/models/stock_backorder_confirmation.py b/indoteknik_custom/models/stock_backorder_confirmation.py
new file mode 100644
index 00000000..0fd7c34e
--- /dev/null
+++ b/indoteknik_custom/models/stock_backorder_confirmation.py
@@ -0,0 +1,50 @@
+from odoo import models, fields, api
+from odoo.tools.float_utils import float_compare
+
+class StockBackorderConfirmation(models.TransientModel):
+ _inherit = 'stock.backorder.confirmation'
+
+ def process(self):
+ res = super(StockBackorderConfirmation, self).process()
+
+ pickings_to_do = self.env['stock.picking']
+ for line in self.backorder_confirmation_line_ids:
+ if line.to_backorder:
+ pickings_to_do |= line.picking_id
+
+ for pick in pickings_to_do:
+ # Mencari backorder yang baru terbentuk
+ backorder = self.env['stock.picking'].search([('backorder_id', '=', pick.id)], limit=1)
+
+ if backorder:
+ # Cari BU/OUT terbaru berdasarkan sale_id
+ latest_out_picking = self.env['stock.picking'].search([
+ ('sale_id', '=', pick.sale_id.id),
+ ('picking_type_id.code', '=', 'outgoing')
+ ], order='id desc', limit=1)
+
+ # Update linked_out_picking_id pada backorder BU/PICK
+ if latest_out_picking:
+ backorder.linked_out_picking_id = latest_out_picking.id
+ else:
+ backorder.linked_out_picking_id = pick.linked_out_picking_id
+
+ # 🚀 Cek apakah ada backorder baru dari BU/OUT
+ for pick in self.env['stock.picking'].search([
+ ('picking_type_id.code', '=', 'outgoing'),
+ ('backorder_id', '!=', False)
+ ]):
+ # Backorder BU/OUT terbaru
+ latest_out_backorder = self.env['stock.picking'].search([
+ ('backorder_id', '=', pick.id)
+ ], order='id desc', limit=1)
+
+ if latest_out_backorder:
+ # 🚀 Update semua BU/PICK yang belum `done` atau `cancel`
+ self.env['stock.picking'].search([
+ ('sale_id', '=', pick.sale_id.id),
+ ('picking_type_id.code', '=', 'incoming'),
+ ('state', 'not in', ['done', 'cancel'])
+ ]).write({'linked_out_picking_id': latest_out_backorder.id})
+
+ return res
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 3f888b02..02ce819f 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -124,7 +124,7 @@ class StockPicking(models.Model):
('cancel', 'Cancelled'),
], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.")
notee = fields.Text(string="Note")
- quantity_koli = fields.Float(string="Quantity Koli")
+ quantity_koli = fields.Float(string="Quantity Koli", copy=False)
source_koli_id = fields.Many2one('stock.picking', string="Source Koli")
@api.model
@@ -170,6 +170,54 @@ class StockPicking(models.Model):
lalamove_image_url = fields.Char(string="Lalamove Image URL")
lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html")
+ total_so_koli = fields.Integer(compute='_compute_total_so_koli', string="Total SO Koli")
+ total_koli = fields.Integer(compute='_compute_total_koli', string="Total Koli")
+ total_koli_display = fields.Char(compute='_compute_total_koli_display', string="Total Koli Display")
+ linked_out_picking_id = fields.Many2one('stock.picking', string="Linked BU/OUT", copy=False)
+ backorder_picking_id = fields.Many2one('stock.picking', string="Backorder Picking", copy=False)
+
+ def action_create_backorder(self):
+ """ Override method to handle backorder logic automatically """
+ backorder = super(StockPicking, self).action_create_backorder()
+
+ for picking in self:
+ if 'BU/PICK/' in picking.name:
+ # Jika BU/PICK memiliki BU/OUT yang terhubung
+ if picking.linked_out_picking_id:
+ out_picking = picking.linked_out_picking_id
+ out_backorder = out_picking.backorder_picking_id
+
+ # Jika BU/OUT belum punya backorder, hubungkan BU/PICK backorder ke BU/OUT lama
+ if not out_backorder:
+ backorder.linked_out_picking_id = out_picking
+ else:
+ # Jika BU/OUT sudah punya backorder, hubungkan ke backorder BU/OUT
+ backorder.linked_out_picking_id = out_backorder
+
+ elif 'BU/OUT/' in picking.name:
+ # Jika BU/OUT membuat backorder, update semua BU/PICK yang terhubung ke BU/OUT lama
+ pickings_to_update = self.env['stock.picking'].search([('linked_out_picking_id', '=', picking.id)])
+ for pick in pickings_to_update:
+ pick.linked_out_picking_id = backorder
+
+ return backorder
+
+
+ @api.depends('total_so_koli') # Sesuaikan dengan field yang relevan
+ def _compute_total_so_koli(self):
+ for picking in self:
+ picking.total_so_koli = self.env['check.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id)])
+
+ @api.depends('total_koli') # Sesuaikan dengan field yang relevan
+ def _compute_total_koli(self):
+ for picking in self:
+ picking.total_koli = self.env['scan.koli'].search_count([('picking_id', '=', picking.id)])
+
+ @api.depends('total_koli', 'total_so_koli')
+ def _compute_total_koli_display(self):
+ for picking in self:
+ picking.total_koli_display = f"{picking.total_koli} / {picking.total_so_koli}"
+
@api.onchange('quantity_koli')
def _onchange_quantity_koli(self):
self.check_koli_lines = [(5, 0, 0)]
@@ -772,6 +820,9 @@ class StockPicking(models.Model):
raise UserError('Quantity Done melebihi Quantity Onhand')
def button_validate(self):
+ if self.total_koli != self.total_so_koli:
+ raise UserError(_("Total Koli (%s) dan Total SO Koli (%s) tidak sama! Harap periksa kembali.")
+ % (self.total_koli, self.total_so_koli))
if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking':
if self.origin and 'Return of' in self.origin:
raise UserError("Button ini hanya untuk Logistik")
@@ -819,11 +870,40 @@ class StockPicking(models.Model):
self.validation_minus_onhand_quantity()
self.responsible = self.env.user.id
+ if self.picking_type_code == 'internal' and 'BU/PICK/' in self.name:
+ self.send_koli_to_so()
+ if self.picking_type_code == 'outgoing' and 'BU/OUT/' in self.name:
+ self.check_koli()
res = super(StockPicking, self).button_validate()
self.calculate_line_no()
self.date_done = datetime.datetime.utcnow()
self.state_reserve = 'done'
return res
+
+
+ def check_koli(self):
+ for picking in self:
+ sale_id = picking.sale_id
+ for koli_lines in picking.scan_koli_lines:
+ if koli_lines.koli_id.sale_order_id != sale_id:
+ raise UserError('Koli tidak sesuai')
+
+ def send_koli_to_so(self):
+ for picking in self:
+ for koli_line in picking.check_koli_lines:
+ existing_koli = self.env['sales.order.koli'].search([
+ ('sale_order_id', '=', picking.sale_id.id),
+ ('picking_id', '=', picking.id),
+ ('koli_id', '=', koli_line.id)
+ ], limit=1)
+
+ if not existing_koli: # Hindari duplikasi
+ self.env['sales.order.koli'].create({
+ 'sale_order_id': picking.sale_id.id,
+ 'picking_id': picking.id,
+ 'koli_id': koli_line.id
+ })
+
def check_qty_done_stock(self):
@@ -901,6 +981,34 @@ class StockPicking(models.Model):
res = super(StockPicking, self).action_cancel()
return res
+
+ def write(self, vals_list):
+ """ Override write method to auto-link BU/PICK to BU/OUT when necessary """
+ records = super(StockPicking, self).write(vals_list)
+ for picking in records:
+ if 'BU/OUT/' in picking.name:
+ # Cari BU/PICK yang berhubungan berdasarkan logika tertentu
+ pick_picking = self.env['stock.picking'].search([
+ ('name', 'like', 'BU/PICK/%'),
+ ('linked_out_picking_id', '=', False),
+ ('sale_id', '=', picking.sale_id.id)
+ ], limit=1)
+
+ if pick_picking:
+ pick_picking.linked_out_picking_id = picking
+
+ if 'BU/PICK/' in picking.name:
+ # Cari BU/PICK yang berhubungan berdasarkan logika tertentu
+ pick_picking = self.env['stock.picking'].search([
+ ('name', 'like', 'BU/OUT/%'),
+ ('state', 'not in', ['cancel', 'done']),
+ ('sale_id', '=', picking.sale_id.id)
+ ], limit=1)
+
+ if pick_picking:
+ pick_picking.linked_out_picking_id = picking
+
+ return records
@api.model
@@ -917,7 +1025,20 @@ class StockPicking(models.Model):
if self.env['stock.picking'].search_count([('name', '=', new_name), ('company_id', '=', vals.get('company_id'))]) > 0:
new_name = f"{new_name}-DUP"
vals['name'] = new_name
- return super(StockPicking, self).create(vals)
+ records = super(StockPicking, self).create(vals)
+ for picking in records:
+ if 'BU/OUT/' in picking.name:
+ # Cari BU/PICK yang berhubungan berdasarkan logika tertentu
+ pick_picking = self.env['stock.picking'].search([
+ ('name', 'like', 'BU/PICK'),
+ ('linked_out_picking_id', '=', False),
+ ('origin', '=', picking.origin)
+ ], limit=1)
+
+ if pick_picking:
+ pick_picking.linked_out_picking_id = picking
+
+ return records
def write(self, vals):
self._use_faktur(vals)
@@ -1251,6 +1372,7 @@ class ScanKoli(models.Model):
_name = 'scan.koli'
_description = 'Scan Koli'
_order = 'picking_id, id'
+ _rec_name = 'koli_id'
picking_id = fields.Many2one(
'stock.picking',
@@ -1260,8 +1382,45 @@ class ScanKoli(models.Model):
index=True,
copy=False,
)
- koli_id = fields.Many2one('check.koli', string='Koli')
+ koli_id = fields.Many2one('sales.order.koli', string='Koli')
+ scan_koli_progress = fields.Char(
+ string="Progress Scan Koli",
+ compute="_compute_scan_koli_progress"
+ )
+
+ def _compute_scan_koli_progress(self):
+ """ Menghitung progres scan koli dalam format 'X/Y' """
+ for scan in self:
+ if scan.picking_id:
+ all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id')
+ scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan
+ total_so_koli = scan.picking_id.total_so_koli
+ scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0"
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ """ Override create untuk update progress scan setelah scan koli ditambahkan """
+ records = super(ScanKoli, self).create(vals_list)
+ for record in records:
+ record._compute_scan_koli_progress()
+ return records
+
+ @api.constrains('picking_id', 'picking_id.total_so_koli')
+ def _check_koli_validation(self):
+ """ Validasi jika jumlah scan koli melebihi total SO koli """
+ for scan in self:
+ total_scans = len(scan.picking_id.scan_koli_lines)
+ if total_scans > scan.picking_id.total_so_koli:
+ raise UserError(_("Jumlah scan koli melebihi total SO koli!"))
+
+ @api.onchange('koli_id')
+ def _onchange_koli_id(self):
+ if not self.koli_id:
+ return
+
+ source_koli_so = self.picking_id.ids # Picking asal dari Koli yang dipilih
+ source_koli = self.koli_id.picking_id.linked_out_picking_id.ids
- @api.constrains('koli_id')
- def _constrains_koli_id(self):
- self.picking_id.source_koli_id = self.koli_id.picking_id.id \ No newline at end of file
+ # Cek apakah source_koli ditemukan
+ if source_koli_so != source_koli:
+ raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!'))
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index fa126492..3040ff2f 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -155,6 +155,8 @@ access_scan_koli,access.scan.koli,model_scan_koli,,1,1,1,1
access_stock_immediate_transfer,access.stock.immediate.transfer,model_stock_immediate_transfer,,1,1,1,1
access_coretax_faktur,access.coretax.faktur,model_coretax_faktur,,1,1,1,1
access_purchase_order_unlock_wizard,access.purchase.order.unlock.wizard,model_purchase_order_unlock_wizard,,1,1,1,1
+access_sales_order_koli,access.sales.order.koli,model_sales_order_koli,,1,1,1,1
+access_stock_backorder_confirmation,access.stock.backorder.confirmation,model_stock_backorder_confirmation,,1,1,1,1
access_User_pengajuan_tempo_line,access.user.pengajuan.tempo.line,model_user_pengajuan_tempo_line,,1,1,1,1
access_user_pengajuan_tempo,access.user.pengajuan.tempo,model_user_pengajuan_tempo,,1,1,1,1
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 008a04ed..877208b0 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -280,6 +280,9 @@
<page string="Reject Line" name="page_sale_order_reject_line">
<field name="reject_line" readonly="1"/>
</page>
+ <page string="Koli" name="page_sales_order_koli_line">
+ <field name="koli_lines" readonly="1"/>
+ </page>
</page>
</field>
</record>
@@ -392,6 +395,18 @@
</data>
<data>
+ <record id="sales_order_koli_tree" model="ir.ui.view">
+ <field name="name">sales.order.koli.tree</field>
+ <field name="model">sales.order.koli</field>
+ <field name="arch" type="xml">
+ <tree editable="top" create="false" delete="false">
+ <field name="koli_id" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+ </data>
+
+ <data>
</data>
<record id="sales_order_fulfillment_v2_tree" model="ir.ui.view">
diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml
index 42fa481d..2a11459c 100644
--- a/indoteknik_custom/views/stock_picking.xml
+++ b/indoteknik_custom/views/stock_picking.xml
@@ -73,7 +73,10 @@
<field name="dokumen_tanda_terima"/>
<field name="dokumen_pengiriman"/>
<field name="quantity_koli" attrs="{'invisible': [('location_dest_id', '!=', 60)]}"/>
- <field name="source_koli_id" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}"/>
+ <!-- <field name="source_koli_id" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}"/> -->
+ <field name="total_koli_display" readonly="1"/>
+ <field name="linked_out_picking_id" readonly="1"/>
+ <field name="backorder_picking_id" readonly="1"/>
</field>
<field name="weight_uom_name" position="after">
<group>
@@ -217,6 +220,7 @@
<field name="arch" type="xml">
<tree editable="bottom">
<field name="koli_id"/>
+ <field name="scan_koli_progress"/>
</tree>
</field>
</record>