summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-03-03 16:51:05 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-03-03 16:51:05 +0700
commit20a56a76c519eba82f70ea1443e272ac64797dd9 (patch)
tree2f86887d82333e3925c73fe21be44fd2c8a1cb2e
parente94dbdf4418c686ec4e8fdab41d4f05e5284fbfb (diff)
push
-rw-r--r--indoteknik_custom/models/stock_backorder_confirmation.py2
-rw-r--r--indoteknik_custom/models/stock_picking.py195
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv1
-rw-r--r--indoteknik_custom/views/stock_picking.xml49
4 files changed, 181 insertions, 66 deletions
diff --git a/indoteknik_custom/models/stock_backorder_confirmation.py b/indoteknik_custom/models/stock_backorder_confirmation.py
index f4da4cb5..d8a41f54 100644
--- a/indoteknik_custom/models/stock_backorder_confirmation.py
+++ b/indoteknik_custom/models/stock_backorder_confirmation.py
@@ -9,7 +9,7 @@ class StockBackorderConfirmation(models.TransientModel):
pickings_not_to_do = self.env['stock.picking']
for line in self.backorder_confirmation_line_ids:
line.picking_id.send_mail_bills()
- line.picking_id.send_koli_to_so()
+ # line.picking_id.send_koli_to_so()
if line.to_backorder is True:
pickings_to_do |= line.picking_id
else:
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index bbd9043d..dfc33caa 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -1,10 +1,10 @@
from odoo import fields, models, api, _
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tools.float_utils import float_is_zero
+from collections import defaultdict
from datetime import timedelta, datetime
from itertools import groupby
import pytz, requests, json, requests
-from collections import defaultdict
from dateutil import parser
import datetime
import hmac
@@ -127,6 +127,8 @@ class StockPicking(models.Model):
notee = fields.Text(string="Note")
quantity_koli = fields.Float(string="Quantity Koli", copy=False)
+
+
@api.model
def _compute_dokumen_tanda_terima(self):
for picking in self:
@@ -178,7 +180,10 @@ class StockPicking(models.Model):
@api.depends('total_so_koli')
def _compute_total_so_koli(self):
for picking in self:
- picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '!=', 'delivered')])
+ if picking.state == 'done':
+ picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '=', 'delivered')])
+ else:
+ picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '!=', 'delivered')])
@api.depends('total_koli')
def _compute_total_koli(self):
@@ -190,6 +195,24 @@ class StockPicking(models.Model):
for picking in self:
picking.total_koli_display = f"{picking.total_koli} / {picking.total_so_koli}"
+ @api.constrains('quantity_koli')
+ def _constrains_quantity_koli(self):
+ for picking in self:
+ if not picking.linked_out_picking_id:
+ so_koli = self.env['sales.order.koli'].search([('picking_id', '=', picking.id)])
+
+ if so_koli:
+ so_koli.unlink()
+
+ for rec in picking.check_koli_lines:
+ self.env['sales.order.koli'].create({
+ 'sale_order_id': picking.sale_id.id,
+ 'picking_id': picking.id,
+ 'koli_id': rec.id,
+ })
+ else:
+ raise UserError('Tidak Bisa Mengubah Quantity Koli Karena Koli Dari Picking Ini Sudah Dipakai Di BU/OUT!')
+
@api.onchange('quantity_koli')
def _onchange_quantity_koli(self):
self.check_koli_lines = [(5, 0, 0)]
@@ -850,6 +873,31 @@ class StockPicking(models.Model):
self.date_done = datetime.datetime.utcnow()
self.state_reserve = 'done'
self.send_koli_to_so()
+ if not self.env.context.get('skip_koli_check'):
+ for picking in self:
+ if picking.sale_id:
+ all_koli_ids = picking.sale_id.koli_lines.filtered(lambda k: k.state != 'delivered').ids
+ scanned_koli_ids = picking.scan_koli_lines.mapped('koli_id.id')
+
+ missing_koli_ids = set(all_koli_ids) - set(scanned_koli_ids)
+
+ if len(missing_koli_ids) > 0 and picking.picking_type_code == 'outgoing' and 'BU/OUT/' in picking.name:
+ missing_koli_names = picking.sale_id.koli_lines.filtered(lambda k: k.id in missing_koli_ids and k.state != 'delivered').mapped('display_name')
+ missing_koli_list = "\n".join(f"- {name}" for name in missing_koli_names)
+
+ # Buat wizard modal warning
+ wizard = self.env['warning.modal.wizard'].create({
+ 'message': f"Berikut Koli yang belum discan:\n{missing_koli_list}",
+ 'picking_id': picking.id,
+ })
+
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'warning.modal.wizard',
+ 'view_mode': 'form',
+ 'res_id': wizard.id,
+ 'target': 'new',
+ }
return res
@@ -876,15 +924,16 @@ class StockPicking(models.Model):
'picking_id': picking.id,
'koli_id': koli_line.id
})
-
+
if picking.picking_type_code == 'outgoing' and 'BU/OUT/' in picking.name:
- for koli_line in picking.scan_koli_lines:
- existing_koli = self.env['sales.order.koli'].search([
- ('sale_order_id', '=', picking.sale_id.id),
- ('koli_id', '=', koli_line.koli_id.koli_id.id)
- ], limit=1)
-
- existing_koli.state = 'delivered'
+ if picking.state == 'done':
+ for koli_line in picking.scan_koli_lines:
+ existing_koli = self.env['sales.order.koli'].search([
+ ('sale_order_id', '=', picking.sale_id.id),
+ ('koli_id', '=', koli_line.koli_id.koli_id.id)
+ ], limit=1)
+
+ existing_koli.state = 'delivered'
def check_qty_done_stock(self):
for line in self.move_line_ids_without_package:
@@ -1319,78 +1368,83 @@ class ScanKoli(models.Model):
)
def unlink(self):
- picking_ids = set(self.mapped('picking_id.id')) # Tangkap picking_id sebelum hapus scan.koli
-
+ picking_ids = set(self.mapped('koli_id.picking_id.id')) # Ambil semua picking_id yang terpengaruh
for scan in self:
koli = scan.koli_id.koli_id
if koli:
+ # Hapus reserved_id saat scan dihapus
koli.reserved_id = False
- # Jika tidak ada scan.koli lain untuk picking_id yang sama, reset linked_out_picking_id
- if not self.env['scan.koli'].search_count([
- ('id', '!=', scan.id),
- ('koli_id.picking_id', '=', koli.picking_id.id)
- ]):
- koli.picking_id.linked_out_picking_id = False
-
- result = super(ScanKoli, self).unlink() # Hapus scan.koli
-
- # Reset qty_done jika semua scan.koli untuk picking_id tersebut telah dihapus
+ # Periksa ulang apakah masih ada scan.koli yang tersisa untuk setiap picking_id
for picking_id in picking_ids:
- self._reset_qty_done_if_no_scan(picking_id)
+ remaining_scans = self.env['sales.order.koli'].search_count([
+ ('koli_id.picking_id', '=', picking_id)
+ ])
- return result
+ delete_koli = len(self.filtered(lambda rec: rec.koli_id.picking_id.id == picking_id))
- def _reset_qty_done_if_no_scan(self, picking_id):
- """Set qty_done ke 0 hanya jika tidak ada scan.koli tersisa untuk picking_id tersebut."""
- remaining_scans = self.env['scan.koli'].search_count([('picking_id', '=', picking_id)])
- if remaining_scans == 0:
- picking = self.env['stock.picking'].browse(picking_id)
- picking.move_line_ids_without_package.write({'qty_done': 0})
- picking.message_post(body=f"⚠️ qty_done direset ke 0 untuk Picking {picking.name} karena tidak ada scan.koli yang tersisa.")
+ # Jika tidak ada scan.koli lain yang tersisa, set linked_out_picking_id ke False
+ if remaining_scans == delete_koli:
+ picking = self.env['stock.picking'].browse(picking_id)
+ picking.linked_out_picking_id = False
+ else:
+ raise UserError(_("Tidak dapat menghapus scan koli, karena masih ada scan koli lain yang tersisa untuk picking ini."))
+
+ for picking_id in picking_ids:
+ self._reset_qty_done_if_no_scan(picking_id)
+
+ # self.check_koli_not_balance()
- return remaining_scans
+ return super(ScanKoli, self).unlink()
- @api.onchange('koli_id', 'scan_koli_progress')
+ @api.onchange('koli_id','scan_koli_progress')
def onchange_koli_id(self):
- for scan in self.filtered('koli_id'):
- if scan.koli_id.koli_id.picking_id.group_id.id != scan.picking_id.group_id.id:
+ if not self.koli_id:
+ return
+
+ for scan in self:
+ if scan.koli_id.koli_id.picking_id.group_id.id != scan.picking_id.group_id.id:
scan.koli_id.koli_id.reserved_id = scan.picking_id.id.origin
scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id.origin
- @api.depends('picking_id')
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.picking_id.scan_koli_lines.ids.index(scan.id) + 1}/{total_so_koli}" if total_so_koli else "0/0"
+ scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0"
@api.constrains('picking_id', 'picking_id.total_so_koli')
def _check_koli_validation(self):
- for scan in self:
+ """ Validasi jika jumlah scan koli melebihi total SO koli """
+ for scan in self.picking_id.scan_koli_lines:
scan.koli_id.koli_id.reserved_id = scan.picking_id.id
scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id
- if len(scan.picking_id.scan_koli_lines) != scan.picking_id.total_so_koli:
- raise UserError("Jumlah scan koli tidak sama dengan total SO koli!")
+
+ total_scans = len(self.picking_id.scan_koli_lines)
+ if total_scans != self.picking_id.total_so_koli:
+ raise UserError(_("Jumlah scan koli tidak sama dengan total SO koli!"))
+
+ # def check_koli_not_balance(self):
+ # for scan in self:
+ # total_scancs = self.env['scan.koli'].search_count([('picking_id', '=', scan.picking_id.id), ('id', '!=', scan.id)])
+ # if total_scancs != scan.picking_id.total_so_koli:
+ # raise UserError(_("Jumlah scan koli tidak sama dengan total SO koli!"))
@api.onchange('koli_id')
def _onchange_koli_id(self):
- for scan in self.filtered('koli_id'):
- if scan.picking_id.group_id.id != scan.koli_id.picking_id.group_id.id:
- raise UserError('Koli tidak sesuai, pastikan picking terkait benar!')
-
- @api.onchange('koli_id')
- def _onchange_koliii(self):
- for scan in self.filtered('koli_id'):
- if self.env['scan.koli'].search_count([
- ('picking_id', '=', scan.picking_id.id),
- ('koli_id', '=', scan.koli_id.id),
- ('id', '!=', scan.id.origin)
- ]):
- scan.koli_id = False
- raise UserError(f"Koli {scan.koli_id.name} sudah dipindai dalam picking ini!")
+ if not self.koli_id:
+ return
+ source_koli_so = self.picking_id.group_id.id
+ source_koli = self.koli_id.picking_id.group_id.id
+
+ if source_koli_so != source_koli:
+ raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!'))
+
@api.constrains('koli_id')
def _send_product_from_koli_id(self):
if not self.koli_id:
@@ -1412,4 +1466,35 @@ class ScanKoli(models.Model):
for pick_move in pick_moves:
corresponding_out_move = out_moves.filtered(lambda m: m.product_id == pick_move.product_id)
if corresponding_out_move:
- corresponding_out_move.qty_done = corresponding_out_move.product_uom_qty # Update qty_done \ No newline at end of file
+ corresponding_out_move.qty_done += pick_move.qty_done
+
+ def _reset_qty_done_if_no_scan(self, picking_id):
+ """Set qty_done ke 0 hanya jika tidak ada scan.koli tersisa untuk picking_id tersebut."""
+ product_bu_pick = self.env['stock.move.line'].search([('picking_id', '=', picking_id)])
+
+ for move in product_bu_pick:
+ product_bu_out = self.env['stock.move.line'].search([('picking_id', '=', self.picking_id.id), ('product_id', '=', move.product_id.id)])
+ for bu_out in product_bu_out:
+ bu_out.qty_done -= move.qty_done
+ # if remaining_scans == 0:
+ # picking = self.env['stock.picking'].browse(picking_id)
+ # picking.move_line_ids_without_package.write({'qty_done': 0})
+ # picking.message_post(body=f"⚠ qty_done direset ke 0 untuk Picking {picking.name} karena tidak ada scan.koli yang tersisa.")
+
+ # return remaining_scans
+
+class WarningModalWizard(models.TransientModel):
+ _name = 'warning.modal.wizard'
+ _description = 'Peringatan Koli Belum Diperiksa'
+
+ name = fields.Char(default="⚠️ Perhatian!")
+ message = fields.Text()
+ picking_id = fields.Many2one('stock.picking')
+
+ def action_continue(self):
+ """Lanjutkan validasi setelah menutup wizard"""
+ if self.picking_id:
+ return self.picking_id.with_context(skip_koli_check=True).button_validate()
+ return {'type': 'ir.actions.act_window_close'}
+
+
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index 3040ff2f..83de8bda 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -157,6 +157,7 @@ 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_warning_modal_wizard,access.warning.modal.wizard,model_warning_modal_wizard,,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/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml
index 1b3406ec..016fbf17 100644
--- a/indoteknik_custom/views/stock_picking.xml
+++ b/indoteknik_custom/views/stock_picking.xml
@@ -72,9 +72,9 @@
<field name="count_line_detail"/>
<field name="dokumen_tanda_terima"/>
<field name="dokumen_pengiriman"/>
- <field name="quantity_koli" attrs="{'invisible': [('location_dest_id', '!=', 60)]}"/>
- <field name="total_koli_display" readonly="1"/>
- <field name="linked_out_picking_id" readonly="1"/>
+ <field name="quantity_koli" attrs="{'invisible': [('location_dest_id', '!=', 60)], 'required': [('location_dest_id', '=', 60)]}"/>
+ <field name="total_koli_display" readonly="1" attrs="{'invisible': [('location_id', '!=', 60)]}"/>
+ <field name="linked_out_picking_id" readonly="1" attrs="{'invisible': [('location_id', '=', 60)]}"/>
</field>
<field name="weight_uom_name" position="after">
<group>
@@ -147,7 +147,7 @@
</group>
</group>
</page>
- <page string="Delivery" name="delivery_order">
+ <page string="Delivery" name="delivery_order" attrs="{'invisible': [('location_dest_id', '=', 60)]}">
<group>
<group>
<field name="notee"/>
@@ -195,7 +195,7 @@
</group>
</group>
</page>
- <page string="Check Product" name="check_product">
+ <page string="Check Product" name="check_product" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}">
<field name="check_product_lines"/>
</page>
<page string="Barcode Product" name="barcode_product" attrs="{'invisible': [('picking_type_code', '!=', 'incoming')]}">
@@ -217,7 +217,7 @@
<field name="model">scan.koli</field>
<field name="arch" type="xml">
<tree editable="bottom">
- <field name="koli_id" domain="[('state', '=', 'not_delivered')]"/>
+ <field name="koli_id" options="{'no_create': True}" required="1" domain="[('state', '=', 'not_delivered')]"/>
<field name="scan_koli_progress"/>
</tree>
</field>
@@ -239,9 +239,9 @@
<field name="model">check.product</field>
<field name="arch" type="xml">
<tree editable="bottom" decoration-warning="status == 'Pending'" decoration-success="status == 'Done'">
- <field name="product_id"/>
- <field name="quantity"/>
- <field name="status"/>
+ <field name="product_id" required="1" options="{'no_create': True}"/>
+ <field name="quantity" readonly="1"/>
+ <field name="status" readonly="1"/>
</tree>
</field>
</record>
@@ -278,6 +278,35 @@
<field name="purchase_representative_id"/>
</field>
</field>
- </record>
+ </record>
+
+ <record id="view_warning_modal_wizard_form" model="ir.ui.view">
+ <field name="name">warning.modal.wizard.form</field>
+ <field name="model">warning.modal.wizard</field>
+ <field name="arch" type="xml">
+ <form string="Peringatan Koli Belum Diperiksa">
+ <sheet>
+ <div class="oe_title">
+ <h2><span>⚠️ Perhatian!</span></h2>
+ </div>
+ <group>
+ <field name="message" readonly="1" nolabel="1" widget="text"/>
+ </group>
+ </sheet>
+ <footer>
+ <button name="action_continue" type="object" string="Lanjutkan" class="btn-primary"/>
+ <button string="Tutup" class="btn-secondary" special="cancel"/>
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_warning_modal_wizard" model="ir.actions.act_window">
+ <field name="name">Peringatan Koli</field>
+ <field name="res_model">warning.modal.wizard</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="view_warning_modal_wizard_form"/>
+ <field name="target">new</field>
+ </record>
</data>
</odoo> \ No newline at end of file