summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2025-07-30 10:40:11 +0000
committerIT Fixcomart <it@fixcomart.co.id>2025-07-30 10:40:11 +0000
commitd6bdde8fe63ff15ce8f3fee6bc5e8ae903fc78ce (patch)
tree511e7d518e8794cf100aead7e12826cceb73a1e4
parent29345559644b26f46068ebdc7623204452e43cc3 (diff)
parent6d5435fffa97486b78fd871ed3859acd45830793 (diff)
Merged in tukar_guling (pull request #372)
<miqdad> change status return doc
-rw-r--r--indoteknik_custom/models/stock_picking.py6
-rw-r--r--indoteknik_custom/models/tukar_guling.py87
-rw-r--r--indoteknik_custom/models/tukar_guling_po.py43
-rw-r--r--indoteknik_custom/views/tukar_guling.xml5
-rw-r--r--indoteknik_custom/views/tukar_guling_po.xml3
5 files changed, 118 insertions, 26 deletions
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 5dd5844d..825368de 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -1384,6 +1384,12 @@ class StockPicking(models.Model):
self.send_mail_bills()
if 'BU/PUT' in self.name:
self.automatic_reserve_product()
+
+ if self.tukar_guling_id:
+ self.tukar_guling_id.update_state()
+ elif self.tukar_guling_po_id:
+ self.tukar_guling_po_id.update_state()
+
return res
def automatic_reserve_product(self):
diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py
index 396e4db4..5411b17c 100644
--- a/indoteknik_custom/models/tukar_guling.py
+++ b/indoteknik_custom/models/tukar_guling.py
@@ -5,7 +5,8 @@ from datetime import datetime
_logger = logging.getLogger(__name__)
-#TODO
+
+# TODO
# 1. tracking status dokumen BU [X]
# 2. ganti nama dokumen
# 3. Tracking ketika create dokumen [X]
@@ -20,7 +21,7 @@ class TukarGuling(models.Model):
_order = 'date desc, id desc'
_rec_name = 'name'
_inherit = ['mail.thread', 'mail.activity.mixin']
-
+
partner_id = fields.Many2one('res.partner', string='Customer', readonly=True)
origin = fields.Char(string='Origin SO')
if_so = fields.Boolean('Is SO', default=True)
@@ -62,6 +63,7 @@ class TukarGuling(models.Model):
('approval_sales', ' Approval Sales'),
('approval_finance', 'Approval Finance'),
('approval_logistic', 'Approval Logistic'),
+ ('approved', 'Waiting for Operations'),
('done', 'Done'),
('cancel', 'Canceled')
], default='draft', tracking=True, required=True)
@@ -98,7 +100,7 @@ class TukarGuling(models.Model):
@api.onchange('operations')
def _onchange_operations(self):
"""Auto-populate lines ketika operations dipilih"""
- if self.operations.picking_type_id.id not in [29,30]:
+ if self.operations.picking_type_id.id not in [29, 30]:
raise UserError("❌ Picking type harus BU/OUT atau BU/PICK")
for rec in self:
if rec.operations and rec.operations.picking_type_id.id == 30:
@@ -217,7 +219,6 @@ class TukarGuling(models.Model):
self.origin = False
-
def action_populate_lines(self):
"""Manual button untuk populate lines - sebagai alternatif"""
self.ensure_one()
@@ -257,7 +258,7 @@ class TukarGuling(models.Model):
def _check_product_lines(self):
"""Constraint: Product lines harus ada jika state bukan draft"""
for record in self:
- if record.state in ('approval_sales', 'approval_logistic', 'approval_finance',
+ if record.state in ('approval_sales', 'approval_logistic', 'approval_finance', 'approved',
'done') and not record.line_ids:
raise ValidationError("Product lines harus diisi sebelum submit atau approve!")
@@ -345,7 +346,7 @@ class TukarGuling(models.Model):
def write(self, vals):
self.ensure_one()
- if self.operations.picking_type_id.id not in [29,30]:
+ if self.operations.picking_type_id.id not in [29, 30]:
raise UserError("❌ Picking type harus BU/OUT atau BU/PICK")
self._check_invoice_on_revisi_so()
operasi = self.operations.picking_type_id.id
@@ -376,10 +377,10 @@ class TukarGuling(models.Model):
# if self.state == 'done':
# raise UserError ("Tidak Boleh delete ketika sudahh done")
for record in self:
- if record.state == 'done':
+ if record.state == 'approved' or record.state == 'done':
raise UserError(
- "Tidak bisa hapus pengajuan jika sudah done, set ke draft terlebih dahulu jika ingin menghapus")
- ongoing_bu = self.picking_ids.filtered(lambda p: p.state != 'done')
+ "Tidak bisa hapus pengajuan jika sudah Approved, set ke draft terlebih dahulu jika ingin menghapus")
+ ongoing_bu = self.picking_ids.filtered(lambda p: p.state != 'approved')
for picking in ongoing_bu:
picking.action_cancel()
return super(TukarGuling, self).unlink()
@@ -461,6 +462,47 @@ class TukarGuling(models.Model):
raise UserError("Submit hanya bisa dilakukan dari Draft.")
self.state = 'approval_sales'
+ def update_state(self):
+ # OUT tukar guling
+ if self.operations.picking_type_id.id == 29 and self.return_type == 'tukar_guling':
+ total_out = self.env['stock.picking'].search_count([
+ ('tukar_guling_id', '=', self.id),
+ ('picking_type_id', '=', 29),
+ ])
+ done_out = self.env['stock.picking'].search_count([
+ ('tukar_guling_id', '=', self.id),
+ ('picking_type_id', '=', 29),
+ ('state', '=', 'done'),
+ ])
+ if self.state == 'approved' and total_out > 0 and done_out == total_out:
+ self.state = 'done'
+
+ # OUT revisi SO
+ elif self.operations.picking_type_id.id == 29 and self.return_type == 'revisi_so':
+ total_ort = self.env['stock.picking'].search_count([
+ ('tukar_guling_id', '=', self.id),
+ ('picking_type_id', '=', 74),
+ ])
+ done_ort = self.env['stock.picking'].search_count([
+ ('tukar_guling_id', '=', self.id),
+ ('picking_type_id', '=', 74),
+ ('state', '=', 'done'),
+ ])
+ if self.state == 'approved' and total_ort > 0 and done_ort == total_ort:
+ self.state = 'done'
+
+ # PICK revisi SO
+ elif self.operations.picking_type_id.id == 30 and self.return_type == 'revisi_so':
+ done_ort = self.env['stock.picking'].search([
+ ('tukar_guling_id', '=', self.id),
+ ('picking_type_id', '=', 74),
+ ('state', '=', 'done'),
+ ])
+ if self.state == 'approved' and done_ort:
+ self.state = 'done'
+ else:
+ raise UserError("Tidak bisa menentukan jenis retur.")
+
def action_approve(self):
self.ensure_one()
self._validate_product_lines()
@@ -510,7 +552,7 @@ class TukarGuling(models.Model):
elif rec.state == 'approval_logistic':
if not rec.env.user.has_group('indoteknik_custom.group_role_logistic'):
raise UserError("Hanya Logistic Manager yang boleh approve tahap ini.")
- rec.state = 'done'
+ rec.state = 'approved'
rec._create_pickings()
rec.date_logistic = now
@@ -606,7 +648,8 @@ class TukarGuling(models.Model):
### ======== ORT dari BU/PICK =========
ort_pickings = []
is_retur_from_bu_pick = record.operations.picking_type_id.id == 30
- picks_to_return = [record.operations] if is_retur_from_bu_pick else mapping_koli.mapped('pick_id') or line.product_uom_qty
+ picks_to_return = [record.operations] if is_retur_from_bu_pick else mapping_koli.mapped(
+ 'pick_id') or line.product_uom_qty
for pick in picks_to_return:
ort_return_lines = []
@@ -622,7 +665,8 @@ class TukarGuling(models.Model):
'quantity': line.product_uom_qty,
'move_id': move.id,
}))
- _logger.info(f"📟 ORT (BU/PICK langsung) | {pick.name} | {line.product_id.display_name} | qty={line.product_uom_qty}")
+ _logger.info(
+ f"📟 ORT (BU/PICK langsung) | {pick.name} | {line.product_id.display_name} | qty={line.product_uom_qty}")
else:
# Ambil dari mapping koli
for mk in mapping_koli.filtered(lambda m: m.pick_id == pick):
@@ -635,7 +679,8 @@ class TukarGuling(models.Model):
'quantity': mk.qty_return,
'move_id': move.id,
}))
- _logger.info(f"📟 ORT (mapping koli) | {pick.name} | {mk.product_id.display_name} | qty={mk.qty_return}")
+ _logger.info(
+ f"📟 ORT (mapping koli) | {pick.name} | {mk.product_id.display_name} | qty={mk.qty_return}")
if ort_return_lines:
ort_wizard = self.env['stock.return.picking'].with_context({
@@ -817,18 +862,17 @@ class StockPicking(models.Model):
message = _(
"📦 <b>%s</b> Validated by <b>%s</b> Status Changed <b>%s</b> at <b>%s</b>."
) % (
- picking.name,
- # picking.picking_type_id.name,
- picking.env.user.name,
- picking.state,
- fields.Datetime.now().strftime("%d/%m/%Y %H:%M")
- )
+ picking.name,
+ # picking.picking_type_id.name,
+ picking.env.user.name,
+ picking.state,
+ fields.Datetime.now().strftime("%d/%m/%Y %H:%M")
+ )
picking.tukar_guling_id.message_post(body=message)
return res
-
class TukarGulingMappingKoli(models.Model):
_name = 'tukar.guling.mapping.koli'
_description = 'Mapping Koli di Tukar Guling'
@@ -839,6 +883,7 @@ class TukarGulingMappingKoli(models.Model):
qty_done = fields.Float(string='Qty Done BU PICK')
qty_return = fields.Float(string='Qty diretur')
sequence = fields.Integer(string='Sequence', default=10)
+
@api.constrains('qty_return')
def _check_qty_return_editable(self):
for rec in self:
@@ -849,4 +894,4 @@ class TukarGulingMappingKoli(models.Model):
for rec in self:
if rec.tukar_guling_id and rec.tukar_guling_id.state not in ['draft', 'cancel']:
raise UserError("Tidak bisa menghapus Mapping Koli karena status Tukar Guling bukan Draft atau Cancel.")
- return super(TukarGulingMappingKoli, self).unlink() \ No newline at end of file
+ return super(TukarGulingMappingKoli, self).unlink()
diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py
index 11377bc4..23ca1923 100644
--- a/indoteknik_custom/models/tukar_guling_po.py
+++ b/indoteknik_custom/models/tukar_guling_po.py
@@ -49,6 +49,7 @@ class TukarGulingPO(models.Model):
('approval_purchase', 'Approval Purchasing'),
('approval_finance', 'Approval Finance'),
('approval_logistic', 'Approval Logistic'),
+ ('approved', 'Waiting for Operations'),
('done', 'Done'),
('cancel', 'Cancel'),
], string='Status', default='draft', tracking=3)
@@ -311,7 +312,7 @@ class TukarGulingPO(models.Model):
def unlink(self):
for record in self:
- if record.state == 'done':
+ if record.state == 'done' or record.state == 'approved':
raise UserError("Tidak bisa hapus pengajuan jika sudah done, set ke draft terlebih dahulu")
ongoing_bu = self.po_picking_ids.filtered(lambda p: p.state != 'done')
for picking in ongoing_bu:
@@ -412,12 +413,50 @@ class TukarGulingPO(models.Model):
elif rec.state == 'approval_logistic':
if not rec.env.user.has_group('indoteknik_custom.group_role_logistic'):
raise UserError("Hanya Logistic Manager yang boleh approve tahap ini.")
- rec.state = 'done'
+ rec.state = 'approved'
rec._create_pickings()
rec.date_logistic = now
else:
raise UserError("Status ini tidak bisa di-approve.")
+ def update_stae(self):
+ # bu input rev po
+ if self.operations.picking_type_id.id == 28 and self.return_type == 'revisi_po':
+ prt = self.env['stock.picking'].search([
+ ('tukar_guling_po_id', '=', self.id),
+ ('state', '=', 'done'),
+ ('picking_type_id.id', '=', 76)
+ ])
+ if self.state == 'aproved' and prt:
+ self.state = 'done'
+ # bu put rev po
+ elif self.operations.picking_type_id.id == 75 and self.return_type == 'revisi_po':
+ total_prt = self.env['stock.picking'].search_count([
+ ('tukar_guling_po_id', '=', self.id),
+ ('picking_type_id.id', '=', 76)
+ ])
+ prt = self.env['stock.picking'].search_count([
+ ('tukar_guling_po_id', '=', self.id),
+ ('state', '=', 'done'),
+ ('picking_type_id.id', '=', 76)
+ ])
+ if self.state == 'aproved' and total_prt > 0 and prt == total_prt:
+ self.state = 'done'
+ # bu put tukar guling
+ elif self.operations.picking_type_id.id == 75 and self.return_type == 'tukar_guling':
+ total_put = self.env['stock.picking'].search_count([
+ ('tukar_guling_po_id', '=', self.id),
+ ('picking_type_id.id', '=', 75)
+ ])
+ put = self.env['stock.picking'].search_count([
+ ('tukar_guling_po_id', '=', self.id),
+ ('state', '=', 'done'),
+ ('picking_type_id.id', '=', 75)
+ ])
+ if self.state == 'aproved' and total_put > 0 and put == total_put:
+ self.state = 'done'
+
+
def action_cancel(self):
self.ensure_one()
# if self.state == 'done':
diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml
index fa3db0d2..c23995d3 100644
--- a/indoteknik_custom/views/tukar_guling.xml
+++ b/indoteknik_custom/views/tukar_guling.xml
@@ -29,6 +29,7 @@
<field name="return_type" string="Return Type"/>
<field name="state" widget="badge"
decoration-info="state in ('draft', 'approval_sales', 'approval_finance','approval_logistic')"
+ decoration-warning="state == 'approved'"
decoration-success="state == 'done'"
decoration-muted="state == 'cancel'"
/>
@@ -58,7 +59,7 @@
class="btn-secondary"
attrs="{'invisible': [('state', '!=', 'cancel')]}"/>
<field name="state" widget="statusbar" readonly="1"
- statusbar_visible="draft,approval_sales,approval_logistic,approval_finance,done"/>
+ statusbar_visible="draft,approval_sales,approval_logistic,approval_finance,approved,done"/>
</header>
<sheet>
<div class="oe_button_box">
@@ -66,7 +67,7 @@
type="object"
class="oe_stat_button"
icon="fa-truck"
- attrs="{'invisible': [('picking_ids', '=', False), ('state', 'in', ['draft', 'approval_sales', 'approval_logistic', 'approval_finance'])]}">
+ attrs="{'invisible': [('picking_ids', '=', False), ('state', 'in', ['draft', 'approval_sales', 'approval_logistic', 'approval_finance', 'approved', 'done', 'cancel'])]}">
<field name="picking_ids" widget="statinfo" string="Delivery"/>
</button>
</div>
diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml
index 26c0a0d4..accf7dbc 100644
--- a/indoteknik_custom/views/tukar_guling_po.xml
+++ b/indoteknik_custom/views/tukar_guling_po.xml
@@ -29,6 +29,7 @@
<field name="return_type" string="Return Type"/>
<field name="state" widget="badge"
decoration-info="state in ('draft', 'approval_purchase', 'approval_finance','approval_logistic')"
+ decoration-warning="state == 'approved'"
decoration-success="state == 'done'"
decoration-muted="state == 'cancel'"
/>
@@ -60,7 +61,7 @@
attrs="{'invisible': [('state', '!=', 'cancel')]}"
confirm="Are you sure you want to reset this record to draft?"/>
<field name="state" widget="statusbar" readonly="1"
- statusbar_visible="draft,approval_purchase,approval_logistic,approval_finance,done"/>
+ statusbar_visible="draft,approval_purchase,approval_logistic,approval_finance,approved,done"/>
</header>
<sheet>
<div class="oe_button_box">