From 8a025fe63ea44b93d3978da7df3aa31533da5300 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 13 Jun 2025 09:13:08 +0700 Subject: add tukar guling model and view --- indoteknik_custom/models/tukar_guling.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 indoteknik_custom/models/tukar_guling.py (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 860abd78b0474279f851378f0b6507fe71fd76be Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 13 Jun 2025 11:39:00 +0700 Subject: PTG document name sequence --- indoteknik_custom/models/tukar_guling.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index e69de29b..ca246c7f 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -0,0 +1,21 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError, ValidationError + + +class TukarGuling(models.Model): + _name = 'tukar.guling' + _description = 'Tukar Guling' + _order = 'date desc, id desc' + _rec_name = 'name' + + # Hanya 2 field seperti yang Anda inginkan + name = fields.Char('Reference', required=True, copy=False, readonly=True, default='New') + date = fields.Datetime('Date', default=fields.Datetime.now, required=True) + + # Sequence generator + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get('name', 'New') == 'New': + vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or '0' + return super(TukarGuling, self).create(vals_list) \ No newline at end of file -- cgit v1.2.3 From d2c05ac97f195e196605d91088d6f76d9312e528 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 13 Jun 2025 17:19:28 +0700 Subject: match with meet --- indoteknik_custom/models/tukar_guling.py | 141 +++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 5 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index ca246c7f..e214e268 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -8,14 +8,145 @@ class TukarGuling(models.Model): _order = 'date desc, id desc' _rec_name = 'name' - # Hanya 2 field seperti yang Anda inginkan - name = fields.Char('Reference', required=True, copy=False, readonly=True, default='New') + name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) + out_num = fields.Many2one('stock.picking', 'Nomor BU/Out', required=True, + domain=[('picking_type_id.code', '=', 'outgoing')]) + ba_num = fields.Text('Nomor BA') + notes = fields.Text('Notes') + return_type = fields.Selection(String='Return Type', selection=[ + ('tukar_guling', 'Tukar Guling'), + ('revisi_so', 'Revisi SO'), + ('revisi_po', 'Revisi PO'), + ('credit_memo', 'Credit Memo'), + ('debit_memo', 'Debit Memo'), + ('lain_lain', 'Lain-lain')]) + + # ✅ PERBAIKAN: Ganti 'states' dengan 'state' + state = fields.Selection(string='Status', selection=[ + ('draft', 'Draft'), + ('waiting', 'Waiting for Approval'), + ('done', 'Done'), + ('cancel', 'Canceled') + ], default='draft', tracking=True, required=True) + + # ✅ NEW: Line items + line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') + + @api.constrains('line_ids', 'state') + def _check_product_lines(self): + """Constraint: Product lines harus ada jika state bukan draft""" + for record in self: + if record.state in ('waiting', 'done') and not record.line_ids: + raise ValidationError("Product lines harus diisi sebelum submit atau approve!") + + def _validate_product_lines(self): + """Helper method untuk validasi product lines""" + self.ensure_one() + + # Check ada product lines + if not self.line_ids: + raise UserError("Belum ada product lines yang ditambahkan!") + + # Check product sudah diisi + empty_lines = self.line_ids.filtered(lambda line: not line.product_id) + if empty_lines: + raise UserError("Ada product lines yang belum diisi productnya!") + + # Check quantity > 0 + zero_qty_lines = self.line_ids.filtered(lambda line: line.product_uom_qty <= 0) + if zero_qty_lines: + raise UserError("Quantity product tidak boleh kosong atau 0!") + + return True - # Sequence generator @api.model_create_multi def create(self, vals_list): for vals in vals_list: if vals.get('name', 'New') == 'New': - vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or '0' - return super(TukarGuling, self).create(vals_list) \ No newline at end of file + vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'PTG/00001' + return super(TukarGuling, self).create(vals_list) + + def copy(self, default=None): + """Override copy untuk custom behavior saat duplicate""" + if default is None: + default = {} + + # Reset fields penting saat duplicate + default.update({ + 'name': 'New', # Akan auto-generate sequence baru + 'state': 'draft', + 'date': fields.Datetime.now(), + # ba_num dan out_num tidak di-reset, user bisa edit manual + }) + + # Copy record dengan default values + new_record = super(TukarGuling, self).copy(default) + + # Re-sequence line items untuk record baru + if new_record.line_ids: + for i, line in enumerate(new_record.line_ids): + line.sequence = (i + 1) * 10 + + return new_record + + def action_draft(self): + """Reset to draft state""" + for record in self: + if record.state == 'cancel': + record.write({'state': 'draft'}) + else: + raise UserError("Hanya record yang di-cancel yang bisa dikembalikan ke draft") + + def action_submit(self): + self.ensure_one() + if self.state != 'draft': + raise UserError("Hanya status Draft saja yang bisa di submit") + self.state = 'waiting' + + def action_approve(self): + self.ensure_one() + if self.state != 'waiting': + raise UserError("Hanya status Waiting saja yang bisa di approve") + self.state = 'done' + + def action_cancel(self): + self.ensure_one() + if self.state == 'done': + raise UserError("Tidak bisa cancel jika sudah done") + self.state = 'cancel' + + +class TukarGulingLine(models.Model): + _name = 'tukar.guling.line' + _description = 'Tukar Guling Line' + _order = 'sequence, id' + + sequence = fields.Integer('Sequence', default=10, copy=False) + tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling', required=True, ondelete='cascade') + product_id = fields.Many2one('product.product', string='Product', required=True) + product_uom_qty = fields.Float('Quantity', digits='Product Unit of Measure', required=True, default=1.0) + product_uom = fields.Many2one('uom.uom', string='Unit of Measure') + name = fields.Text('Description') + + @api.model_create_multi + def create(self, vals_list): + """Override create to auto-assign sequence""" + for vals in vals_list: + if 'sequence' not in vals or vals.get('sequence', 0) <= 0: + # Get max sequence untuk tukar_guling yang sama + tukar_guling_id = vals.get('tukar_guling_id') + if tukar_guling_id: + max_seq = self.search([ + ('tukar_guling_id', '=', tukar_guling_id) + ], order='sequence desc', limit=1) + vals['sequence'] = (max_seq.sequence or 0) + 10 + else: + vals['sequence'] = 10 + return super(TukarGulingLine, self).create(vals_list) + + @api.onchange('product_id') + def _onchange_product_id(self): + if self.product_id: + self.name = self.product_id.display_name + self.product_uom = self.product_id.uom_id \ No newline at end of file -- cgit v1.2.3 From f5fc52453c0f7e52b6c87fa57d2de5120c3041e0 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 14 Jun 2025 10:05:15 +0700 Subject: check validation --- indoteknik_custom/models/tukar_guling.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index e214e268..36819ad4 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -22,7 +22,6 @@ class TukarGuling(models.Model): ('debit_memo', 'Debit Memo'), ('lain_lain', 'Lain-lain')]) - # ✅ PERBAIKAN: Ganti 'states' dengan 'state' state = fields.Selection(string='Status', selection=[ ('draft', 'Draft'), ('waiting', 'Waiting for Approval'), @@ -30,7 +29,6 @@ class TukarGuling(models.Model): ('cancel', 'Canceled') ], default='draft', tracking=True, required=True) - # ✅ NEW: Line items line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') @api.constrains('line_ids', 'state') @@ -44,6 +42,7 @@ class TukarGuling(models.Model): """Helper method untuk validasi product lines""" self.ensure_one() + # Check ada product lines if not self.line_ids: raise UserError("Belum ada product lines yang ditambahkan!") @@ -74,16 +73,15 @@ class TukarGuling(models.Model): # Reset fields penting saat duplicate default.update({ - 'name': 'New', # Akan auto-generate sequence baru + 'name': 'New', 'state': 'draft', 'date': fields.Datetime.now(), - # ba_num dan out_num tidak di-reset, user bisa edit manual }) # Copy record dengan default values new_record = super(TukarGuling, self).copy(default) - # Re-sequence line items untuk record baru + # Re-sequence line items record baru if new_record.line_ids: for i, line in enumerate(new_record.line_ids): line.sequence = (i + 1) * 10 @@ -100,12 +98,29 @@ class TukarGuling(models.Model): def action_submit(self): self.ensure_one() + # cek bu out sudah diisi atau blm + if not self.out_num: + raise UserError("BU/Out harus diisi!") + + # cek return type + # if not self.return_type: + # raise UserError("Return Type harus diisi!") + if self.state != 'draft': raise UserError("Hanya status Draft saja yang bisa di submit") self.state = 'waiting' def action_approve(self): self.ensure_one() + + # cek bu out sudah diisi atau blm + if not self.out_num: + raise UserError("BU/Out harus diisi!") + + # cek return type + if not self.return_type: + raise UserError("Return Type harus diisi!") + if self.state != 'waiting': raise UserError("Hanya status Waiting saja yang bisa di approve") self.state = 'done' -- cgit v1.2.3 From 50769e870756dd350421a205e6d49ab555023764 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sun, 15 Jun 2025 17:33:46 +0700 Subject: fix state view and add ingoing field --- indoteknik_custom/models/tukar_guling.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 36819ad4..b4a901d0 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -10,18 +10,17 @@ class TukarGuling(models.Model): name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) - out_num = fields.Many2one('stock.picking', 'Nomor BU/Out', required=True, + out_num = fields.Many2one('stock.picking', 'Nomor BU/Out', domain=[('picking_type_id.code', '=', 'outgoing')]) + in_num = fields.Many2one('stock.picking', 'Nomor BU/In', domain=[('picking_type_id.code', '=', 'incoming')]) ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ - ('tukar_guling', 'Tukar Guling'), - ('revisi_so', 'Revisi SO'), + ('tukar_guling', 'Tukar Guling'), # -> barang yang sama + ('revisi_so', 'Revisi SO'), # -> ganti barang ? ('revisi_po', 'Revisi PO'), - ('credit_memo', 'Credit Memo'), - ('debit_memo', 'Debit Memo'), - ('lain_lain', 'Lain-lain')]) - + ('credit_memo', 'Credit Memo'), # -> dijadiin credit memo + ('debit_memo', 'Debit Memo')]) state = fields.Selection(string='Status', selection=[ ('draft', 'Draft'), ('waiting', 'Waiting for Approval'), @@ -31,6 +30,14 @@ class TukarGuling(models.Model): line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') + @api.constrains('return_type', 'out_num', 'in_num') + def _check_return_type_fields(self): + for rec in self: + if rec.return_type in ['tukar_guling', 'credit_memo', 'revisi_so'] and not rec.out_num: + raise ValidationError("Field BU/Out wajib diisi untuk jenis return type tersebut.") + if rec.return_type in ['debit_memo', 'revisi_po'] and not rec.in_num: + raise ValidationError("Field BU/In wajib diisi untuk jenis return type tersebut.") + @api.constrains('line_ids', 'state') def _check_product_lines(self): """Constraint: Product lines harus ada jika state bukan draft""" -- cgit v1.2.3 From 32f33ae528f4f9883f38b8afba2ce79222eed4e5 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 16 Jun 2025 07:50:57 +0700 Subject: tukar guling fill bu/in or bu/out --- indoteknik_custom/models/tukar_guling.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index b4a901d0..f62206e8 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -30,13 +30,17 @@ class TukarGuling(models.Model): line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') - @api.constrains('return_type', 'out_num', 'in_num') - def _check_return_type_fields(self): - for rec in self: - if rec.return_type in ['tukar_guling', 'credit_memo', 'revisi_so'] and not rec.out_num: - raise ValidationError("Field BU/Out wajib diisi untuk jenis return type tersebut.") - if rec.return_type in ['debit_memo', 'revisi_po'] and not rec.in_num: - raise ValidationError("Field BU/In wajib diisi untuk jenis return type tersebut.") + @api.onchange('return_type') + def _onchange_return_type(self): + domain = [] + if self.return_type in ['debit_memo', 'revisi_po']: + domain = [('picking_type_id.code', '=', 'incoming')] + elif self.return_type in ['revisi_so', 'credit_memo']: + domain = [('picking_type_id.code', '=', 'outgoing')] + elif self.return_type == 'tukar_guling': + domain = [('picking_type_id.code', 'in', ['incoming', 'outgoing'])] + + return {'domain': {'in_num': domain}} @api.constrains('line_ids', 'state') def _check_product_lines(self): -- cgit v1.2.3 From 0354e469c6761964ecfc68208f1ad9a521610d56 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 16 Jun 2025 07:54:40 +0700 Subject: tukar guling fill bu/in or bu/out --- indoteknik_custom/models/tukar_guling.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index f62206e8..4f27afde 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -32,15 +32,28 @@ class TukarGuling(models.Model): @api.onchange('return_type') def _onchange_return_type(self): - domain = [] + in_domain = [] + out_domain = [] + if self.return_type in ['debit_memo', 'revisi_po']: - domain = [('picking_type_id.code', '=', 'incoming')] + # Hanya tampilkan BU In + in_domain = [('picking_type_id.code', '=', 'incoming')] + out_domain = [('id', '=', False)] # Kosongkan BU Out elif self.return_type in ['revisi_so', 'credit_memo']: - domain = [('picking_type_id.code', '=', 'outgoing')] + # Hanya tampilkan BU Out + in_domain = [('id', '=', False)] # Kosongkan BU In + out_domain = [('picking_type_id.code', '=', 'outgoing')] elif self.return_type == 'tukar_guling': - domain = [('picking_type_id.code', 'in', ['incoming', 'outgoing'])] - - return {'domain': {'in_num': domain}} + # Boleh pilih keduanya + in_domain = [('picking_type_id.code', '=', 'incoming')] + out_domain = [('picking_type_id.code', '=', 'outgoing')] + + return { + 'domain': { + 'in_num': in_domain, + 'out_num': out_domain, + } + } @api.constrains('line_ids', 'state') def _check_product_lines(self): -- cgit v1.2.3 From 402085ff18942c21d1a61eb02f16497c845694b5 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 16 Jun 2025 07:56:51 +0700 Subject: validation --- indoteknik_custom/models/tukar_guling.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 4f27afde..27d4d954 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -55,6 +55,13 @@ class TukarGuling(models.Model): } } + @api.constrains('return_type', 'in_num', 'out_num') + def _check_bu_required_for_tukar_guling(self): + for record in self: + if record.return_type == 'tukar_guling': + if not record.in_num and not record.out_num: + raise ValidationError("Untuk Tukar Guling, isi salah satu: BU/In atau BU/Out.") + @api.constrains('line_ids', 'state') def _check_product_lines(self): """Constraint: Product lines harus ada jika state bukan draft""" -- cgit v1.2.3 From 5ed938e0386e64733b90d8a4b08b0a0a5b4bc00e Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 16 Jun 2025 07:58:56 +0700 Subject: validation --- indoteknik_custom/models/tukar_guling.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 27d4d954..f8cbec0a 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -56,11 +56,16 @@ class TukarGuling(models.Model): } @api.constrains('return_type', 'in_num', 'out_num') - def _check_bu_required_for_tukar_guling(self): + def _check_required_bu_fields(self): for record in self: - if record.return_type == 'tukar_guling': - if not record.in_num and not record.out_num: - raise ValidationError("Untuk Tukar Guling, isi salah satu: BU/In atau BU/Out.") + if record.return_type in ['debit_memo', 'revisi_po'] and not record.in_num: + raise ValidationError("BU/In harus diisi untuk return type Debit Memo atau Revisi PO.") + + if record.return_type in ['revisi_so', 'credit_memo'] and not record.out_num: + raise ValidationError("BU/Out harus diisi untuk return type Revisi SO atau Credit Memo.") + + if record.return_type == 'tukar_guling' and not (record.in_num or record.out_num): + raise ValidationError("Untuk Tukar Guling, minimal isi salah satu, BU/In atau BU/Out.") @api.constrains('line_ids', 'state') def _check_product_lines(self): -- cgit v1.2.3 From a0e90200638e26ad06d1caaf2d91d0aeea3ba19d Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 16 Jun 2025 14:38:40 +0700 Subject: add tukar guling PO and move from inventory --- indoteknik_custom/models/tukar_guling.py | 211 +++++++++++++++++++++---------- 1 file changed, 146 insertions(+), 65 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index f8cbec0a..95aa7cd6 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -12,66 +12,34 @@ class TukarGuling(models.Model): date = fields.Datetime('Date', default=fields.Datetime.now, required=True) out_num = fields.Many2one('stock.picking', 'Nomor BU/Out', domain=[('picking_type_id.code', '=', 'outgoing')]) - in_num = fields.Many2one('stock.picking', 'Nomor BU/In', domain=[('picking_type_id.code', '=', 'incoming')]) ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ ('tukar_guling', 'Tukar Guling'), # -> barang yang sama ('revisi_so', 'Revisi SO'), # -> ganti barang ? - ('revisi_po', 'Revisi PO'), - ('credit_memo', 'Credit Memo'), # -> dijadiin credit memo - ('debit_memo', 'Debit Memo')]) + ('credit_memo', 'Credit Memo')]) # -> dijadiin credit memo state = fields.Selection(string='Status', selection=[ ('draft', 'Draft'), - ('waiting', 'Waiting for Approval'), + ('approval_sales', ' Approval Sales'), + ('approval_logistic', 'Approval Logistic'), + ('approval_finance', 'Approval Finance'), ('done', 'Done'), ('cancel', 'Canceled') ], default='draft', tracking=True, required=True) line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') - @api.onchange('return_type') - def _onchange_return_type(self): - in_domain = [] - out_domain = [] - - if self.return_type in ['debit_memo', 'revisi_po']: - # Hanya tampilkan BU In - in_domain = [('picking_type_id.code', '=', 'incoming')] - out_domain = [('id', '=', False)] # Kosongkan BU Out - elif self.return_type in ['revisi_so', 'credit_memo']: - # Hanya tampilkan BU Out - in_domain = [('id', '=', False)] # Kosongkan BU In - out_domain = [('picking_type_id.code', '=', 'outgoing')] - elif self.return_type == 'tukar_guling': - # Boleh pilih keduanya - in_domain = [('picking_type_id.code', '=', 'incoming')] - out_domain = [('picking_type_id.code', '=', 'outgoing')] - - return { - 'domain': { - 'in_num': in_domain, - 'out_num': out_domain, - } - } - - @api.constrains('return_type', 'in_num', 'out_num') + @api.constrains('return_type', 'out_num') def _check_required_bu_fields(self): for record in self: - if record.return_type in ['debit_memo', 'revisi_po'] and not record.in_num: - raise ValidationError("BU/In harus diisi untuk return type Debit Memo atau Revisi PO.") - - if record.return_type in ['revisi_so', 'credit_memo'] and not record.out_num: - raise ValidationError("BU/Out harus diisi untuk return type Revisi SO atau Credit Memo.") - - if record.return_type == 'tukar_guling' and not (record.in_num or record.out_num): - raise ValidationError("Untuk Tukar Guling, minimal isi salah satu, BU/In atau BU/Out.") + if record.return_type in ['revisi_so', 'credit_memo', 'tukar_guling'] and not record.out_num: + raise ValidationError("BU/Out harus diisi!") @api.constrains('line_ids', 'state') def _check_product_lines(self): """Constraint: Product lines harus ada jika state bukan draft""" for record in self: - if record.state in ('waiting', 'done') and not record.line_ids: + if record.state in ('approval_sales', 'approval_logistic', 'approval_finance', 'done') and not record.line_ids: raise ValidationError("Product lines harus diisi sebelum submit atau approve!") def _validate_product_lines(self): @@ -95,29 +63,29 @@ class TukarGuling(models.Model): return True - @api.model_create_multi - def create(self, vals_list): - for vals in vals_list: - if vals.get('name', 'New') == 'New': - vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'PTG/00001' - return super(TukarGuling, self).create(vals_list) + @api.model + def create(self, vals): + if not vals.get('name') or vals['name'] == 'New': + vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'New' + return super(TukarGuling, self).create(vals) def copy(self, default=None): - """Override copy untuk custom behavior saat duplicate""" if default is None: default = {} - # Reset fields penting saat duplicate + if 'name' not in default: + default.update({ + 'name': self.env['ir.sequence'].next_by_code(self._name) or 'New', + }) + default.update({ - 'name': 'New', 'state': 'draft', 'date': fields.Datetime.now(), }) - # Copy record dengan default values new_record = super(TukarGuling, self).copy(default) - # Re-sequence line items record baru + # Re-sequence lines if new_record.line_ids: for i, line in enumerate(new_record.line_ids): line.sequence = (i + 1) * 10 @@ -134,40 +102,153 @@ class TukarGuling(models.Model): def action_submit(self): self.ensure_one() - # cek bu out sudah diisi atau blm + + if self.state != 'draft': + raise UserError("Submit hanya bisa dilakukan dari Draft.") + self.state = 'approval_sales' + + def action_approve(self): + self.ensure_one() + if not self.out_num: raise UserError("BU/Out harus diisi!") - # cek return type - # if not self.return_type: - # raise UserError("Return Type harus diisi!") + if not self.return_type: + raise UserError("Return Type harus diisi!") + + # Cek hak akses berdasarkan state + if self.state == 'approval_sales': + if not self.env.user.has_group('indoteknik_custom.group_sales_manager'): + raise UserError("Hanya Sales Manager yang boleh approve tahap ini.") + self.state = 'approval_logistic' + + elif self.state == 'approval_logistic': + if not self.env.user.has_group('indoteknik_custom.group_logistic'): + raise UserError("Hanya Logistic Manager yang boleh approve tahap ini.") + self.state = 'approval_finance' + + elif self.state == 'approval_finance': + if not self.env.user.has_group('indoteknik_custom.group_finance'): + raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") + self.state = 'done' + + else: + raise UserError("Status ini tidak bisa di-approve.") + def action_cancel(self): + self.ensure_one() + # if self.state == 'done': + # raise UserError("Tidak bisa cancel jika sudah done") + self.state = 'cancel' + +class TukarGulingPO(models.Model): + _name = 'tukar.guling.po' + _inherit = 'tukar.guling' + _description = 'Tukar Guling PO' + + # tukar_guling_id = fields.Many2one( + # 'tukar.guling', required=True, ondelete='cascade', string='Tukar Guling Ref' + # ) + + return_type = fields.Selection([ + ('tukar_guling', 'Tukar Guling'), + ('revisi_po', 'Revisi PO'), + ('debit_memo', 'Debit Memo'), + ], string='Return Type', required=True) + + @api.constrains('return_type', 'out_num') + def _check_required_bu_fields(self): + for record in self: + if record.return_type in ['tukar_guling', 'revisi_po', 'debit_memo'] and not record.out_num: + raise ValidationError("BU/Out harus diisi!") + + @api.constrains('line_ids', 'state') + 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', 'done') and not record.line_ids: + raise ValidationError("Product lines harus diisi sebelum submit atau approve!") + + def _validate_product_lines(self): + """Helper method untuk validasi product lines""" + self.ensure_one() + + # Check ada product lines + if not self.line_ids: + raise UserError("Belum ada product lines yang ditambahkan!") + + # Check product sudah diisi + empty_lines = self.line_ids.filtered(lambda line: not line.product_id) + if empty_lines: + raise UserError("Ada product lines yang belum diisi productnya!") + + # Check quantity > 0 + zero_qty_lines = self.line_ids.filtered(lambda line: line.product_uom_qty <= 0) + if zero_qty_lines: + raise UserError("Quantity product tidak boleh kosong atau 0!") + return True + + @api.model + def create(self, vals): + if not vals.get('name') or vals['name'] in ('New', False): + vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling.po') or 'New' + return super(TukarGulingPO, self).create(vals) + def copy(self, default=None): + if default is None: + default = {} + + # Generate sequence satu-satunya di sini + default['name'] = self.env['ir.sequence'].next_by_code('tukar.guling.po') or 'New' + default['state'] = 'draft' + default['date'] = fields.Datetime.now() + + new_record = super(TukarGulingPO, self).copy(default) + + # Re-sequence lines + if new_record.line_ids: + for i, line in enumerate(new_record.line_ids): + line.sequence = (i + 1) * 10 + + return new_record + + def action_draft(self): + """Reset to draft state""" + for record in self: + if record.state == 'cancel': + record.write({'state': 'draft'}) + else: + raise UserError("Hanya record yang di-cancel yang bisa dikembalikan ke draft") + + def action_submit(self): + self.ensure_one() if self.state != 'draft': - raise UserError("Hanya status Draft saja yang bisa di submit") - self.state = 'waiting' + raise UserError("Submit hanya bisa dilakukan dari Draft.") + self.state = 'approval_sales' def action_approve(self): self.ensure_one() - # cek bu out sudah diisi atau blm if not self.out_num: raise UserError("BU/Out harus diisi!") - # cek return type if not self.return_type: raise UserError("Return Type harus diisi!") - if self.state != 'waiting': - raise UserError("Hanya status Waiting saja yang bisa di approve") - self.state = 'done' + if self.state == 'approval_sales': + self.state = 'approval_logistic' + elif self.state == 'approval_logistic': + self.state = 'approval_finance' + elif self.state == 'approval_finance': + self.state = 'done' + else: + raise UserError("Status ini tidak bisa di-approve.") def action_cancel(self): self.ensure_one() - if self.state == 'done': - raise UserError("Tidak bisa cancel jika sudah done") + # if self.state == 'done': + # raise UserError("Tidak bisa cancel jika sudah done") self.state = 'cancel' - class TukarGulingLine(models.Model): _name = 'tukar.guling.line' _description = 'Tukar Guling Line' -- cgit v1.2.3 From abd7da741c6eec02dbefa195b91dbedd70b3323e Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 17 Jun 2025 08:09:57 +0700 Subject: add tukar guling PO and move from inventory --- indoteknik_custom/models/tukar_guling.py | 56 +++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 95aa7cd6..aeb2c9e7 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -142,12 +142,25 @@ class TukarGuling(models.Model): class TukarGulingPO(models.Model): _name = 'tukar.guling.po' - _inherit = 'tukar.guling' _description = 'Tukar Guling PO' - # tukar_guling_id = fields.Many2one( - # 'tukar.guling', required=True, ondelete='cascade', string='Tukar Guling Ref' - # ) + name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') + date = fields.Datetime('Date', default=fields.Datetime.now, required=True) + out_num = fields.Many2one('stock.picking', 'Nomor BU/Out', + domain=[('picking_type_id.code', '=', 'outgoing')]) + ba_num = fields.Text('Nomor BA') + notes = fields.Text('Notes') + state = fields.Selection(string='Status', selection=[ + ('draft', 'Draft'), + ('approval_purchase', ' Approval Purchase'), + ('approval_logistic', 'Approval Logistic'), + ('approval_finance', 'Approval Finance'), + ('done', 'Done'), + ('cancel', 'Canceled') + ], default='draft', tracking=True, required=True) + + line_ids = fields.One2many('tukar.guling.line.po', 'tukar_guling_po_id', string='Product Lines') + tukar_guling_po_id = fields.Many2one('tukar.guling.po', 'Tukar Guling PO') return_type = fields.Selection([ ('tukar_guling', 'Tukar Guling'), @@ -165,7 +178,7 @@ class TukarGulingPO(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', 'done') and not record.line_ids: + if record.state in ('approval_purchase', 'approval_logistic', 'approval_finance', 'done') and not record.line_ids: raise ValidationError("Product lines harus diisi sebelum submit atau approve!") def _validate_product_lines(self): @@ -223,7 +236,7 @@ class TukarGulingPO(models.Model): if self.state != 'draft': raise UserError("Submit hanya bisa dilakukan dari Draft.") - self.state = 'approval_sales' + self.state = 'approval_purchase' def action_approve(self): self.ensure_one() @@ -234,12 +247,21 @@ class TukarGulingPO(models.Model): if not self.return_type: raise UserError("Return Type harus diisi!") - if self.state == 'approval_sales': + if self.state == 'approval_purchase': + if not self.env.user.has_group('indoteknik_custom.group_role_purchasing'): + raise UserError("Hanya Purchasing yang boleh approve tahap ini.") self.state = 'approval_logistic' + elif self.state == 'approval_logistic': + if not self.env.user.has_group('indoteknik_custom.group_role_logistic'): + raise UserError("Hanya Logistic Manager yang boleh approve tahap ini.") self.state = 'approval_finance' + elif self.state == 'approval_finance': + if not self.env.user.has_group('indoteknik_custom.group_role_fat'): + raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") self.state = 'done' + else: raise UserError("Status ini tidak bisa di-approve.") @@ -281,4 +303,22 @@ class TukarGulingLine(models.Model): def _onchange_product_id(self): if self.product_id: self.name = self.product_id.display_name - self.product_uom = self.product_id.uom_id \ No newline at end of file + self.product_uom = self.product_id.uom_id + +class TukarGulingLinePO(models.Model): + _name = 'tukar.guling.line.po' + _description = 'Tukar Guling Line (PO)' + _order = 'sequence, id' + + tukar_guling_po_id = fields.Many2one('tukar.guling.po', string='Tukar Guling PO', required=True, ondelete='cascade') + sequence = fields.Integer('Sequence', default=10, copy=False) + product_id = fields.Many2one('product.product', string='Product', required=True) + product_uom_qty = fields.Float('Quantity', digits='Product Unit of Measure', required=True, default=1.0) + product_uom = fields.Many2one('uom.uom', string='Unit of Measure') + name = fields.Text('Description') + + @api.onchange('product_id') + def _onchange_product_id(self): + if self.product_id: + self.name = self.product_id.display_name + self.product_uom = self.product_id.uom_id -- cgit v1.2.3 From 3a76bd301734621831f291228deaa962c144be5e Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 17 Jun 2025 08:38:21 +0700 Subject: fix group role --- indoteknik_custom/models/tukar_guling.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index aeb2c9e7..5e814459 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -118,17 +118,17 @@ class TukarGuling(models.Model): # Cek hak akses berdasarkan state if self.state == 'approval_sales': - if not self.env.user.has_group('indoteknik_custom.group_sales_manager'): + if not self.env.user.has_group('indoteknik_custom.group_role_sales'): raise UserError("Hanya Sales Manager yang boleh approve tahap ini.") self.state = 'approval_logistic' elif self.state == 'approval_logistic': - if not self.env.user.has_group('indoteknik_custom.group_logistic'): + if not self.env.user.has_group('indoteknik_custom.group_role_logistic'): raise UserError("Hanya Logistic Manager yang boleh approve tahap ini.") self.state = 'approval_finance' elif self.state == 'approval_finance': - if not self.env.user.has_group('indoteknik_custom.group_finance'): + if not self.env.user.has_group('indoteknik_custom.group_role_fat'): raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") self.state = 'done' -- cgit v1.2.3 From 34d276fe64a92c4a1c75f6fbf8fa961e84c8afc4 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 17 Jun 2025 22:52:39 +0700 Subject: push --- indoteknik_custom/models/tukar_guling.py | 135 +++++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 17 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 5e814459..29670f5c 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -8,6 +8,13 @@ class TukarGuling(models.Model): _order = 'date desc, id desc' _rec_name = 'name' + real_shipping_id = fields.Many2one('res.partner', string='Shipping Address') + + picking_ids = fields.One2many( + 'stock.picking', + 'tukar_guling_id', + string='Transfers' + ) name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) out_num = fields.Many2one('stock.picking', 'Nomor BU/Out', @@ -92,6 +99,17 @@ class TukarGuling(models.Model): return new_record + def action_view_picking(self): + self.ensure_one() + action = self.env.ref('stock.action_picking_tree_all').read()[0] + pickings = self.picking_ids + if len(pickings) > 1: + action['domain'] = [('id', 'in', pickings.ids)] + elif pickings: + action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')] + action['res_id'] = pickings.id + return action + def action_draft(self): """Reset to draft state""" for record in self: @@ -117,29 +135,107 @@ class TukarGuling(models.Model): raise UserError("Return Type harus diisi!") # Cek hak akses berdasarkan state - if self.state == 'approval_sales': - if not self.env.user.has_group('indoteknik_custom.group_role_sales'): - raise UserError("Hanya Sales Manager yang boleh approve tahap ini.") - self.state = 'approval_logistic' - - elif self.state == 'approval_logistic': - if not self.env.user.has_group('indoteknik_custom.group_role_logistic'): - raise UserError("Hanya Logistic Manager yang boleh approve tahap ini.") - self.state = 'approval_finance' - - elif self.state == 'approval_finance': - if not self.env.user.has_group('indoteknik_custom.group_role_fat'): - raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") - self.state = 'done' - - else: - raise UserError("Status ini tidak bisa di-approve.") + for rec in self: + if rec.state == 'approval_sales': + if not rec.env.user.has_group('indoteknik_custom.group_role_sales'): + raise UserError("Hanya Sales Manager yang boleh approve tahap ini.") + rec.state = 'approval_logistic' + + 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 = 'approval_finance' + + elif rec.state == 'approval_finance': + if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): + raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") + rec.state = 'done' + rec._create_pickings() + else: + raise UserError("Status ini tidak bisa di-approve.") def action_cancel(self): self.ensure_one() # if self.state == 'done': # raise UserError("Tidak bisa cancel jika sudah done") self.state = 'cancel' + def _create_pickings(self): + if not self.out_num: + raise UserError("BU/Out harus diisi terlebih dahulu.") + + group_id = self.out_num.group_id.id if self.out_num.group_id else False + + Picking = self.env['stock.picking'] + srt_type = self.env['stock.picking.type'].search([ + ('sequence_code', '=', 'SRT') + ], limit=1) + + ort_type = self.env['stock.picking.type'].search([ + ('sequence_code', '=', 'ORT') + ], limit=1) + + # Lokasi + location_dest_id = srt_type.default_location_dest_id.id + location_customer = self.out_num.location_dest_id + + # 1. BU/SRT: retur dari out_num + srt_picking = Picking.create({ + 'partner_id': self.out_num.partner_id.id, + 'picking_type_id': srt_type.id, + 'location_id': location_customer.id, + 'location_dest_id': location_dest_id, + 'origin': f"Retur {self.out_num.name}", + 'tukar_guling_id': self.id, + 'group_id': group_id, + 'move_ids_without_package': [ + (0, 0, { + 'name': line.name or line.product_id.name, + 'product_id': line.product_id.id, + 'product_uom_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'location_id': location_customer.id, + 'location_dest_id': location_dest_id, + }) for line in self.line_ids + ] + }) + srt_picking.action_confirm() + + # 2. Cari BU/PICK dari SO yang sama + origin_so = self.out_num.origin + if not origin_so: + raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") + + pick = Picking.search([ + ('origin', '=', origin_so), + ('picking_type_id.code', '=', 'internal') + ], limit=1) + + if not pick: + raise UserError(f"BU/PICK dengan origin {origin_so} tidak ditemukan.") + + # 3. BU/ORT: retur dari BU/PICK + ort_picking = Picking.create({ + 'partner_id': self.out_num.partner_id.id, + 'picking_type_id': ort_type.id, + 'location_id': location_dest_id, + 'location_dest_id': location_customer.id, + 'origin': f"Retur {pick.name}", + 'tukar_guling_id': self.id, + 'group_id': group_id, + 'move_ids_without_package': [ + (0, 0, { + 'name': line.name or line.product_id.name, + 'product_id': line.product_id.id, + 'product_uom_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'location_id': location_dest_id, + 'location_dest_id': location_customer.id, + }) for line in self.line_ids + ] + }) + ort_picking.action_confirm() + + class TukarGulingPO(models.Model): _name = 'tukar.guling.po' _description = 'Tukar Guling PO' @@ -322,3 +418,8 @@ class TukarGulingLinePO(models.Model): if self.product_id: self.name = self.product_id.display_name self.product_uom = self.product_id.uom_id + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + origin_tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') \ No newline at end of file -- cgit v1.2.3 From 2a349bbe7d5317433e339d873bdaa46e9d37ae17 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 18 Jun 2025 08:15:56 +0700 Subject: Add origin SO --- indoteknik_custom/models/tukar_guling.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 29670f5c..c1672b73 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -8,6 +8,8 @@ class TukarGuling(models.Model): _order = 'date desc, id desc' _rec_name = 'name' + origin = fields.Char(string='Origin SO') + real_shipping_id = fields.Many2one('res.partner', string='Shipping Address') picking_ids = fields.One2many( @@ -74,6 +76,11 @@ class TukarGuling(models.Model): def create(self, vals): if not vals.get('name') or vals['name'] == 'New': vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'New' + # Auto-fill origin from out_num + if not vals.get('origin') and vals.get('out_num'): + picking = self.env['stock.picking'].browse(vals['out_num']) + if picking.origin: + vals['origin'] = picking.origin return super(TukarGuling, self).create(vals) def copy(self, default=None): @@ -99,6 +106,14 @@ class TukarGuling(models.Model): return new_record + def write(self, vals): + if 'out_num' in vals and not vals.get('origin'): + picking = self.env['stock.picking'].browse(vals['out_num']) + if picking.origin: + vals['origin'] = picking.origin + + return super(TukarGuling, self).write(vals) + def action_view_picking(self): self.ensure_one() action = self.env.ref('stock.action_picking_tree_all').read()[0] -- cgit v1.2.3 From 968d9987eb53670f0d96209e77debb1196f9b939 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 18 Jun 2025 09:33:37 +0700 Subject: tukar guling so done --- indoteknik_custom/models/tukar_guling.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index c1672b73..7ed6e10f 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -1,6 +1,8 @@ from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError +import logging +_logger = logging.getLogger(__name__) class TukarGuling(models.Model): _name = 'tukar.guling' @@ -178,7 +180,19 @@ class TukarGuling(models.Model): if not self.out_num: raise UserError("BU/Out harus diisi terlebih dahulu.") - group_id = self.out_num.group_id.id if self.out_num.group_id else False + origin_so = self.out_num.origin + if not origin_so: + raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") + + # Cari DO dari SO + get_group_id = self.env['stock.picking'].search([ + ('origin', '=', origin_so), + ], limit=1) + + if not get_group_id: + raise UserError(f"Delivery Order dari SO {origin_so} tidak ditemukan.") + + group_id = get_group_id.group_id.id if get_group_id.group_id else False Picking = self.env['stock.picking'] srt_type = self.env['stock.picking.type'].search([ @@ -191,6 +205,7 @@ class TukarGuling(models.Model): # Lokasi location_dest_id = srt_type.default_location_dest_id.id + location_dest_id_ort = ort_type.default_location_dest_id.id location_customer = self.out_num.location_dest_id # 1. BU/SRT: retur dari out_num @@ -210,6 +225,7 @@ class TukarGuling(models.Model): 'product_uom': line.product_uom.id, 'location_id': location_customer.id, 'location_dest_id': location_dest_id, + 'group_id': group_id, }) for line in self.line_ids ] }) @@ -233,7 +249,7 @@ class TukarGuling(models.Model): 'partner_id': self.out_num.partner_id.id, 'picking_type_id': ort_type.id, 'location_id': location_dest_id, - 'location_dest_id': location_customer.id, + 'location_dest_id': location_dest_id_ort, 'origin': f"Retur {pick.name}", 'tukar_guling_id': self.id, 'group_id': group_id, @@ -244,7 +260,8 @@ class TukarGuling(models.Model): 'product_uom_qty': line.product_uom_qty, 'product_uom': line.product_uom.id, 'location_id': location_dest_id, - 'location_dest_id': location_customer.id, + 'location_dest_id': location_dest_id_ort, + 'group_id': group_id, }) for line in self.line_ids ] }) -- cgit v1.2.3 From d1ff4bd35deac6c17a17e97f0904f67e113c5add Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 18 Jun 2025 13:32:05 +0700 Subject: revisi, fetch item from bu out in tukar guling line --- indoteknik_custom/models/tukar_guling.py | 141 +++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 28 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 7ed6e10f..a5724104 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -4,6 +4,7 @@ import logging _logger = logging.getLogger(__name__) + class TukarGuling(models.Model): _name = 'tukar.guling' _description = 'Tukar Guling' @@ -21,14 +22,13 @@ class TukarGuling(models.Model): ) name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) - out_num = fields.Many2one('stock.picking', 'Nomor BU/Out', - domain=[('picking_type_id.code', '=', 'outgoing')]) + operations = fields.Many2one('stock.picking', 'Operations', + domain=[('picking_type_id.code', '=', 'outgoing')], help='Nomor BU/Out atau BU/Pick') ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ - ('tukar_guling', 'Tukar Guling'), # -> barang yang sama - ('revisi_so', 'Revisi SO'), # -> ganti barang ? - ('credit_memo', 'Credit Memo')]) # -> dijadiin credit memo + ('tukar_guling', 'Tukar Guling'), # -> barang yang sama + ('revisi_so', 'Revisi SO')]) state = fields.Selection(string='Status', selection=[ ('draft', 'Draft'), ('approval_sales', ' Approval Sales'), @@ -40,24 +40,108 @@ class TukarGuling(models.Model): line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') - @api.constrains('return_type', 'out_num') + @api.onchange('operations') + def _onchange_operations(self): + """Auto-populate lines ketika operations dipilih""" + if self.operations: + # Clear existing lines + self.line_ids = [(5, 0, 0)] + + # Set origin dari operations + if self.operations.origin: + self.origin = self.operations.origin + + # Auto-populate lines dari move_ids operations + lines_data = [] + sequence = 10 + + # Untuk Odoo 14, gunakan move_ids_without_package atau move_lines + moves_to_check = [] + + # 1. move_ids_without_package (standard di Odoo 14) + if hasattr(self.operations, 'move_ids_without_package') and self.operations.move_ids_without_package: + moves_to_check = self.operations.move_ids_without_package + # 2. move_lines (backup untuk versi lama) + elif hasattr(self.operations, 'move_lines') and self.operations.move_lines: + moves_to_check = self.operations.move_lines + + # Debug logging + _logger = logging.getLogger(__name__) + _logger.info(f"BU/OUT: {self.operations.name}, State: {self.operations.state}") + _logger.info(f"Total moves found: {len(moves_to_check)}") + + for move in moves_to_check: + _logger.info( + f"Move: {move.name}, Product: {move.product_id.name if move.product_id else 'No Product'}, Qty: {move.product_uom_qty}, State: {move.state}") + + # Ambil semua move yang ada quantity + if move.product_id and move.product_uom_qty > 0: + lines_data.append((0, 0, { + 'sequence': sequence, + 'product_id': move.product_id.id, + 'product_uom_qty': move.product_uom_qty, + 'product_uom': move.product_uom.id, + 'name': move.name or move.product_id.display_name, + })) + sequence += 10 + + if lines_data: + self.line_ids = lines_data + _logger.info(f"Created {len(lines_data)} lines") + else: + _logger.info("No lines created - no valid moves found") + else: + # Clear lines jika operations dikosongkan + self.line_ids = [(5, 0, 0)] + self.origin = False + + def action_populate_lines(self): + """Manual button untuk populate lines - sebagai alternatif""" + self.ensure_one() + if not self.operations: + raise UserError("Pilih BU/OUT terlebih dahulu!") + + # Clear existing lines + self.line_ids = [(5, 0, 0)] + + lines_data = [] + sequence = 10 + + # Ambil semua stock moves dari operations + for move in self.operations.move_ids: + if move.product_uom_qty > 0: + lines_data.append((0, 0, { + 'sequence': sequence, + 'product_id': move.product_id.id, + 'product_uom_qty': move.product_uom_qty, + 'product_uom': move.product_uom.id, + 'name': move.name or move.product_id.display_name, + })) + sequence += 10 + + if lines_data: + self.line_ids = lines_data + else: + raise UserError("Tidak ditemukan barang di BU/OUT yang dipilih!") + + @api.constrains('return_type', 'operations') def _check_required_bu_fields(self): for record in self: - if record.return_type in ['revisi_so', 'credit_memo', 'tukar_guling'] and not record.out_num: + if record.return_type in ['revisi_so', 'tukar_guling'] and not record.operations: raise ValidationError("BU/Out harus diisi!") @api.constrains('line_ids', 'state') 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', 'done') and not record.line_ids: + if record.state in ('approval_sales', 'approval_logistic', 'approval_finance', + 'done') and not record.line_ids: raise ValidationError("Product lines harus diisi sebelum submit atau approve!") def _validate_product_lines(self): """Helper method untuk validasi product lines""" self.ensure_one() - # Check ada product lines if not self.line_ids: raise UserError("Belum ada product lines yang ditambahkan!") @@ -78,9 +162,9 @@ class TukarGuling(models.Model): def create(self, vals): if not vals.get('name') or vals['name'] == 'New': vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'New' - # Auto-fill origin from out_num - if not vals.get('origin') and vals.get('out_num'): - picking = self.env['stock.picking'].browse(vals['out_num']) + # Auto-fill origin from operations + if not vals.get('origin') and vals.get('operations'): + picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: vals['origin'] = picking.origin return super(TukarGuling, self).create(vals) @@ -109,8 +193,8 @@ class TukarGuling(models.Model): return new_record def write(self, vals): - if 'out_num' in vals and not vals.get('origin'): - picking = self.env['stock.picking'].browse(vals['out_num']) + if 'operations' in vals and not vals.get('origin'): + picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: vals['origin'] = picking.origin @@ -145,7 +229,7 @@ class TukarGuling(models.Model): def action_approve(self): self.ensure_one() - if not self.out_num: + if not self.operations: raise UserError("BU/Out harus diisi!") if not self.return_type: @@ -170,6 +254,7 @@ class TukarGuling(models.Model): rec._create_pickings() else: raise UserError("Status ini tidak bisa di-approve.") + def action_cancel(self): self.ensure_one() # if self.state == 'done': @@ -177,10 +262,10 @@ class TukarGuling(models.Model): self.state = 'cancel' def _create_pickings(self): - if not self.out_num: + if not self.operations: raise UserError("BU/Out harus diisi terlebih dahulu.") - origin_so = self.out_num.origin + origin_so = self.operations.origin if not origin_so: raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") @@ -206,15 +291,15 @@ class TukarGuling(models.Model): # Lokasi location_dest_id = srt_type.default_location_dest_id.id location_dest_id_ort = ort_type.default_location_dest_id.id - location_customer = self.out_num.location_dest_id + location_customer = self.operations.location_dest_id - # 1. BU/SRT: retur dari out_num + # 1. BU/SRT: retur dari operations srt_picking = Picking.create({ - 'partner_id': self.out_num.partner_id.id, + 'partner_id': self.operations.partner_id.id, 'picking_type_id': srt_type.id, 'location_id': location_customer.id, 'location_dest_id': location_dest_id, - 'origin': f"Retur {self.out_num.name}", + 'origin': f"Retur {self.operations.name}", 'tukar_guling_id': self.id, 'group_id': group_id, 'move_ids_without_package': [ @@ -232,7 +317,7 @@ class TukarGuling(models.Model): srt_picking.action_confirm() # 2. Cari BU/PICK dari SO yang sama - origin_so = self.out_num.origin + origin_so = self.operations.origin if not origin_so: raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") @@ -246,7 +331,7 @@ class TukarGuling(models.Model): # 3. BU/ORT: retur dari BU/PICK ort_picking = Picking.create({ - 'partner_id': self.out_num.partner_id.id, + 'partner_id': self.operations.partner_id.id, 'picking_type_id': ort_type.id, 'location_id': location_dest_id, 'location_dest_id': location_dest_id_ort, @@ -266,7 +351,7 @@ class TukarGuling(models.Model): ] }) ort_picking.action_confirm() - + ort_picking.action_assign() class TukarGulingPO(models.Model): _name = 'tukar.guling.po' @@ -274,7 +359,7 @@ class TukarGulingPO(models.Model): name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) - out_num = fields.Many2one('stock.picking', 'Nomor BU/Out', + operations = fields.Many2one('stock.picking', 'Nomor BU/Out', domain=[('picking_type_id.code', '=', 'outgoing')]) ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') @@ -296,10 +381,10 @@ class TukarGulingPO(models.Model): ('debit_memo', 'Debit Memo'), ], string='Return Type', required=True) - @api.constrains('return_type', 'out_num') + @api.constrains('return_type', 'operations') def _check_required_bu_fields(self): for record in self: - if record.return_type in ['tukar_guling', 'revisi_po', 'debit_memo'] and not record.out_num: + if record.return_type in ['tukar_guling', 'revisi_po', 'debit_memo'] and not record.operations: raise ValidationError("BU/Out harus diisi!") @api.constrains('line_ids', 'state') @@ -369,7 +454,7 @@ class TukarGulingPO(models.Model): def action_approve(self): self.ensure_one() - if not self.out_num: + if not self.operations: raise UserError("BU/Out harus diisi!") if not self.return_type: -- cgit v1.2.3 From 594b0a4ac57480ac750b22a8361afb1045e7ad44 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 18 Jun 2025 16:13:57 +0700 Subject: cannot edit line product only delete --- indoteknik_custom/models/tukar_guling.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index a5724104..186cff97 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -296,6 +296,7 @@ class TukarGuling(models.Model): # 1. BU/SRT: retur dari operations srt_picking = Picking.create({ 'partner_id': self.operations.partner_id.id, + 'real_shipping_id': self.operations.real_shipping_id.id, 'picking_type_id': srt_type.id, 'location_id': location_customer.id, 'location_dest_id': location_dest_id, @@ -332,6 +333,7 @@ class TukarGuling(models.Model): # 3. BU/ORT: retur dari BU/PICK ort_picking = Picking.create({ 'partner_id': self.operations.partner_id.id, + 'real_shipping_id': self.operations.real_shipping_id.id, 'picking_type_id': ort_type.id, 'location_id': location_dest_id, 'location_dest_id': location_dest_id_ort, -- cgit v1.2.3 From 1542b2373ef4cff98ded7c9bbf426e18b5524162 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 19 Jun 2025 10:33:02 +0700 Subject: push --- indoteknik_custom/models/tukar_guling.py | 148 ------------------------------- 1 file changed, 148 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 186cff97..08b862a7 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -355,137 +355,6 @@ class TukarGuling(models.Model): ort_picking.action_confirm() ort_picking.action_assign() -class TukarGulingPO(models.Model): - _name = 'tukar.guling.po' - _description = 'Tukar Guling PO' - - name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') - date = fields.Datetime('Date', default=fields.Datetime.now, required=True) - operations = fields.Many2one('stock.picking', 'Nomor BU/Out', - domain=[('picking_type_id.code', '=', 'outgoing')]) - ba_num = fields.Text('Nomor BA') - notes = fields.Text('Notes') - state = fields.Selection(string='Status', selection=[ - ('draft', 'Draft'), - ('approval_purchase', ' Approval Purchase'), - ('approval_logistic', 'Approval Logistic'), - ('approval_finance', 'Approval Finance'), - ('done', 'Done'), - ('cancel', 'Canceled') - ], default='draft', tracking=True, required=True) - - line_ids = fields.One2many('tukar.guling.line.po', 'tukar_guling_po_id', string='Product Lines') - tukar_guling_po_id = fields.Many2one('tukar.guling.po', 'Tukar Guling PO') - - return_type = fields.Selection([ - ('tukar_guling', 'Tukar Guling'), - ('revisi_po', 'Revisi PO'), - ('debit_memo', 'Debit Memo'), - ], string='Return Type', required=True) - - @api.constrains('return_type', 'operations') - def _check_required_bu_fields(self): - for record in self: - if record.return_type in ['tukar_guling', 'revisi_po', 'debit_memo'] and not record.operations: - raise ValidationError("BU/Out harus diisi!") - - @api.constrains('line_ids', 'state') - def _check_product_lines(self): - """Constraint: Product lines harus ada jika state bukan draft""" - for record in self: - if record.state in ('approval_purchase', 'approval_logistic', 'approval_finance', 'done') and not record.line_ids: - raise ValidationError("Product lines harus diisi sebelum submit atau approve!") - - def _validate_product_lines(self): - """Helper method untuk validasi product lines""" - self.ensure_one() - - # Check ada product lines - if not self.line_ids: - raise UserError("Belum ada product lines yang ditambahkan!") - - # Check product sudah diisi - empty_lines = self.line_ids.filtered(lambda line: not line.product_id) - if empty_lines: - raise UserError("Ada product lines yang belum diisi productnya!") - - # Check quantity > 0 - zero_qty_lines = self.line_ids.filtered(lambda line: line.product_uom_qty <= 0) - if zero_qty_lines: - raise UserError("Quantity product tidak boleh kosong atau 0!") - return True - - @api.model - def create(self, vals): - if not vals.get('name') or vals['name'] in ('New', False): - vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling.po') or 'New' - return super(TukarGulingPO, self).create(vals) - def copy(self, default=None): - if default is None: - default = {} - - # Generate sequence satu-satunya di sini - default['name'] = self.env['ir.sequence'].next_by_code('tukar.guling.po') or 'New' - default['state'] = 'draft' - default['date'] = fields.Datetime.now() - - new_record = super(TukarGulingPO, self).copy(default) - - # Re-sequence lines - if new_record.line_ids: - for i, line in enumerate(new_record.line_ids): - line.sequence = (i + 1) * 10 - - return new_record - - def action_draft(self): - """Reset to draft state""" - for record in self: - if record.state == 'cancel': - record.write({'state': 'draft'}) - else: - raise UserError("Hanya record yang di-cancel yang bisa dikembalikan ke draft") - - def action_submit(self): - self.ensure_one() - - if self.state != 'draft': - raise UserError("Submit hanya bisa dilakukan dari Draft.") - self.state = 'approval_purchase' - - def action_approve(self): - self.ensure_one() - - if not self.operations: - raise UserError("BU/Out harus diisi!") - - if not self.return_type: - raise UserError("Return Type harus diisi!") - - if self.state == 'approval_purchase': - if not self.env.user.has_group('indoteknik_custom.group_role_purchasing'): - raise UserError("Hanya Purchasing yang boleh approve tahap ini.") - self.state = 'approval_logistic' - - elif self.state == 'approval_logistic': - if not self.env.user.has_group('indoteknik_custom.group_role_logistic'): - raise UserError("Hanya Logistic Manager yang boleh approve tahap ini.") - self.state = 'approval_finance' - - elif self.state == 'approval_finance': - if not self.env.user.has_group('indoteknik_custom.group_role_fat'): - raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") - self.state = 'done' - - else: - raise UserError("Status ini tidak bisa di-approve.") - - def action_cancel(self): - self.ensure_one() - # if self.state == 'done': - # raise UserError("Tidak bisa cancel jika sudah done") - self.state = 'cancel' - class TukarGulingLine(models.Model): _name = 'tukar.guling.line' _description = 'Tukar Guling Line' @@ -520,23 +389,6 @@ class TukarGulingLine(models.Model): self.name = self.product_id.display_name self.product_uom = self.product_id.uom_id -class TukarGulingLinePO(models.Model): - _name = 'tukar.guling.line.po' - _description = 'Tukar Guling Line (PO)' - _order = 'sequence, id' - - tukar_guling_po_id = fields.Many2one('tukar.guling.po', string='Tukar Guling PO', required=True, ondelete='cascade') - sequence = fields.Integer('Sequence', default=10, copy=False) - product_id = fields.Many2one('product.product', string='Product', required=True) - product_uom_qty = fields.Float('Quantity', digits='Product Unit of Measure', required=True, default=1.0) - product_uom = fields.Many2one('uom.uom', string='Unit of Measure') - name = fields.Text('Description') - - @api.onchange('product_id') - def _onchange_product_id(self): - if self.product_id: - self.name = self.product_id.display_name - self.product_uom = self.product_id.uom_id class StockPicking(models.Model): _inherit = 'stock.picking' -- cgit v1.2.3 From 32724232b991afaff527cf5ff9e58a2cad7ea824 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 19 Jun 2025 13:05:31 +0700 Subject: Fix sequence --- indoteknik_custom/models/tukar_guling.py | 37 ++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 08b862a7..7bcf5e80 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -160,23 +160,34 @@ class TukarGuling(models.Model): @api.model def create(self, vals): + # Generate sequence number if not vals.get('name') or vals['name'] == 'New': - vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'New' + # Pastikan sequence code 'tukar.guling' ada + sequence = self.env['ir.sequence'].search([('code', '=', 'tukar.guling')], limit=1) + if sequence: + vals['name'] = sequence.next_by_id() + else: + # Fallback jika sequence belum dibuat + vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'PTG-00001' + # Auto-fill origin from operations if not vals.get('origin') and vals.get('operations'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: vals['origin'] = picking.origin + return super(TukarGuling, self).create(vals) def copy(self, default=None): if default is None: default = {} - if 'name' not in default: - default.update({ - 'name': self.env['ir.sequence'].next_by_code(self._name) or 'New', - }) + # Generate new sequence untuk duplicate + sequence = self.env['ir.sequence'].search([('code', '=', 'tukar.guling')], limit=1) + if sequence: + default['name'] = sequence.next_by_id() + else: + default['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'PTG-COPY' default.update({ 'state': 'draft', @@ -191,7 +202,6 @@ class TukarGuling(models.Model): line.sequence = (i + 1) * 10 return new_record - def write(self, vals): if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) @@ -352,6 +362,21 @@ class TukarGuling(models.Model): }) for line in self.line_ids ] }) + for line in self.line_ids: + move = ort_picking.move_ids_without_package.filtered( + lambda m: m.product_id == line.product_id + )[:1] + + if move: + self.env['stock.move.line'].create({ + 'move_id': move.id, + 'picking_id': ort_picking.id, + 'product_id': line.product_id.id, + 'product_uom_id': line.product_uom.id, + 'qty_done': line.product_uom_qty, # Ambil dari return.picking.line.quantity + 'location_id': location_customer.id, + 'location_dest_id': location_dest_id, + }) ort_picking.action_confirm() ort_picking.action_assign() -- cgit v1.2.3 From bb8d3f325228936acaad5f8cbdac555fc289b854 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 19 Jun 2025 14:49:03 +0700 Subject: push --- indoteknik_custom/models/tukar_guling.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 7bcf5e80..fe0d6ab0 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -202,6 +202,7 @@ class TukarGuling(models.Model): line.sequence = (i + 1) * 10 return new_record + def write(self, vals): if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) @@ -279,7 +280,7 @@ class TukarGuling(models.Model): if not origin_so: raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") - # Cari DO dari SO + # Cari DO (deliv order (bu/out)) dari SO get_group_id = self.env['stock.picking'].search([ ('origin', '=', origin_so), ], limit=1) @@ -373,7 +374,7 @@ class TukarGuling(models.Model): 'picking_id': ort_picking.id, 'product_id': line.product_id.id, 'product_uom_id': line.product_uom.id, - 'qty_done': line.product_uom_qty, # Ambil dari return.picking.line.quantity + 'qty_done': line.product_uom_qty, 'location_id': location_customer.id, 'location_dest_id': location_dest_id, }) -- cgit v1.2.3 From 1c637ef3fa5f1a0cb39ba0b32353320485622901 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 19 Jun 2025 15:00:29 +0700 Subject: Don, create 4 document --- indoteknik_custom/models/tukar_guling.py | 175 ++++++++++++------------------- 1 file changed, 69 insertions(+), 106 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index fe0d6ab0..6c5d74ec 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -273,113 +273,76 @@ class TukarGuling(models.Model): self.state = 'cancel' def _create_pickings(self): - if not self.operations: - raise UserError("BU/Out harus diisi terlebih dahulu.") - - origin_so = self.operations.origin - if not origin_so: - raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") - - # Cari DO (deliv order (bu/out)) dari SO - get_group_id = self.env['stock.picking'].search([ - ('origin', '=', origin_so), - ], limit=1) - - if not get_group_id: - raise UserError(f"Delivery Order dari SO {origin_so} tidak ditemukan.") - - group_id = get_group_id.group_id.id if get_group_id.group_id else False - Picking = self.env['stock.picking'] - srt_type = self.env['stock.picking.type'].search([ - ('sequence_code', '=', 'SRT') - ], limit=1) - - ort_type = self.env['stock.picking.type'].search([ - ('sequence_code', '=', 'ORT') - ], limit=1) - - # Lokasi - location_dest_id = srt_type.default_location_dest_id.id - location_dest_id_ort = ort_type.default_location_dest_id.id - location_customer = self.operations.location_dest_id - - # 1. BU/SRT: retur dari operations - srt_picking = Picking.create({ - 'partner_id': self.operations.partner_id.id, - 'real_shipping_id': self.operations.real_shipping_id.id, - 'picking_type_id': srt_type.id, - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, - 'origin': f"Retur {self.operations.name}", - 'tukar_guling_id': self.id, - 'group_id': group_id, - 'move_ids_without_package': [ - (0, 0, { - 'name': line.name or line.product_id.name, - 'product_id': line.product_id.id, - 'product_uom_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, - 'group_id': group_id, - }) for line in self.line_ids - ] - }) - srt_picking.action_confirm() - - # 2. Cari BU/PICK dari SO yang sama - origin_so = self.operations.origin - if not origin_so: - raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") - - pick = Picking.search([ - ('origin', '=', origin_so), - ('picking_type_id.code', '=', 'internal') - ], limit=1) - - if not pick: - raise UserError(f"BU/PICK dengan origin {origin_so} tidak ditemukan.") - - # 3. BU/ORT: retur dari BU/PICK - ort_picking = Picking.create({ - 'partner_id': self.operations.partner_id.id, - 'real_shipping_id': self.operations.real_shipping_id.id, - 'picking_type_id': ort_type.id, - 'location_id': location_dest_id, - 'location_dest_id': location_dest_id_ort, - 'origin': f"Retur {pick.name}", - 'tukar_guling_id': self.id, - 'group_id': group_id, - 'move_ids_without_package': [ - (0, 0, { - 'name': line.name or line.product_id.name, - 'product_id': line.product_id.id, - 'product_uom_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'location_id': location_dest_id, - 'location_dest_id': location_dest_id_ort, - 'group_id': group_id, - }) for line in self.line_ids - ] - }) - for line in self.line_ids: - move = ort_picking.move_ids_without_package.filtered( - lambda m: m.product_id == line.product_id - )[:1] - - if move: - self.env['stock.move.line'].create({ - 'move_id': move.id, - 'picking_id': ort_picking.id, - 'product_id': line.product_id.id, - 'product_uom_id': line.product_uom.id, - 'qty_done': line.product_uom_qty, - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, - }) - ort_picking.action_confirm() - ort_picking.action_assign() + group_id = self.env['procurement.group'].create({'name': self.name}).id + + def create_moves(picking_type, origin_suffix, location_id, location_dest_id): + return Picking.create({ + 'partner_id': self.operations.partner_id.id, + 'real_shipping_id': self.operations.real_shipping_id.id, + 'picking_type_id': picking_type.id, + 'location_id': location_id, + 'location_dest_id': location_dest_id, + 'origin': f"Tukar Guling {self.name} - {origin_suffix}", + 'tukar_guling_id': self.id, + 'group_id': group_id, + 'move_ids_without_package': [ + (0, 0, { + 'name': line.name or line.product_id.name, + 'product_id': line.product_id.id, + 'product_uom_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'location_id': location_id, + 'location_dest_id': location_dest_id, + 'group_id': group_id, + }) for line in self.line_ids + ] + }) + + # BU/SRT + srt_type = self.env['stock.picking.type'].search([('sequence_code', '=', 'SRT')], limit=1) + if not srt_type: + raise UserError("Picking Type dengan sequence_code 'SRT' tidak ditemukan.") + bu_srt = create_moves( + srt_type, 'SRT', + self.operations.location_dest_id.id, + srt_type.default_location_dest_id.id + ) + bu_srt.action_confirm() + + # BU/ORT + ort_type = self.env['stock.picking.type'].search([('sequence_code', '=', 'ORT')], limit=1) + if not ort_type: + raise UserError("Picking Type dengan sequence_code 'ORT' tidak ditemukan.") + bu_ort = create_moves( + ort_type, 'ORT', + ort_type.default_location_src_id.id, + ort_type.default_location_dest_id.id + ) + bu_ort.action_confirm() + + # Jika return_type tukar_guling → lanjut buat PICK dan OUT + if self.return_type == 'tukar_guling': + pick_type = self.env['stock.picking.type'].search([('sequence_code', '=', 'PICK')], limit=1) + if not pick_type: + raise UserError("Picking Type dengan sequence_code 'PICK' tidak ditemukan.") + bu_pick = create_moves( + pick_type, 'PICK', + pick_type.default_location_src_id.id, + pick_type.default_location_dest_id.id + ) + bu_pick.action_confirm() + + out_type = self.env['stock.picking.type'].search([('sequence_code', '=', 'OUT')], limit=1) + if not out_type: + raise UserError("Picking Type dengan sequence_code 'OUT' tidak ditemukan.") + bu_out = create_moves( + out_type, 'OUT', + out_type.default_location_src_id.id, + self.operations.location_dest_id.id + ) + bu_out.action_confirm() + class TukarGulingLine(models.Model): _name = 'tukar.guling.line' -- cgit v1.2.3 From bce4d940dc90bf50e045a8fde3fd1c7bb53e8562 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 20 Jun 2025 09:21:19 +0700 Subject: revert --- indoteknik_custom/models/tukar_guling.py | 176 +++++++++++++++++++------------ 1 file changed, 106 insertions(+), 70 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 6c5d74ec..7bcf5e80 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -202,7 +202,6 @@ class TukarGuling(models.Model): line.sequence = (i + 1) * 10 return new_record - def write(self, vals): if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) @@ -273,76 +272,113 @@ class TukarGuling(models.Model): self.state = 'cancel' def _create_pickings(self): - Picking = self.env['stock.picking'] - group_id = self.env['procurement.group'].create({'name': self.name}).id - - def create_moves(picking_type, origin_suffix, location_id, location_dest_id): - return Picking.create({ - 'partner_id': self.operations.partner_id.id, - 'real_shipping_id': self.operations.real_shipping_id.id, - 'picking_type_id': picking_type.id, - 'location_id': location_id, - 'location_dest_id': location_dest_id, - 'origin': f"Tukar Guling {self.name} - {origin_suffix}", - 'tukar_guling_id': self.id, - 'group_id': group_id, - 'move_ids_without_package': [ - (0, 0, { - 'name': line.name or line.product_id.name, - 'product_id': line.product_id.id, - 'product_uom_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'location_id': location_id, - 'location_dest_id': location_dest_id, - 'group_id': group_id, - }) for line in self.line_ids - ] - }) - - # BU/SRT - srt_type = self.env['stock.picking.type'].search([('sequence_code', '=', 'SRT')], limit=1) - if not srt_type: - raise UserError("Picking Type dengan sequence_code 'SRT' tidak ditemukan.") - bu_srt = create_moves( - srt_type, 'SRT', - self.operations.location_dest_id.id, - srt_type.default_location_dest_id.id - ) - bu_srt.action_confirm() - - # BU/ORT - ort_type = self.env['stock.picking.type'].search([('sequence_code', '=', 'ORT')], limit=1) - if not ort_type: - raise UserError("Picking Type dengan sequence_code 'ORT' tidak ditemukan.") - bu_ort = create_moves( - ort_type, 'ORT', - ort_type.default_location_src_id.id, - ort_type.default_location_dest_id.id - ) - bu_ort.action_confirm() - - # Jika return_type tukar_guling → lanjut buat PICK dan OUT - if self.return_type == 'tukar_guling': - pick_type = self.env['stock.picking.type'].search([('sequence_code', '=', 'PICK')], limit=1) - if not pick_type: - raise UserError("Picking Type dengan sequence_code 'PICK' tidak ditemukan.") - bu_pick = create_moves( - pick_type, 'PICK', - pick_type.default_location_src_id.id, - pick_type.default_location_dest_id.id - ) - bu_pick.action_confirm() - - out_type = self.env['stock.picking.type'].search([('sequence_code', '=', 'OUT')], limit=1) - if not out_type: - raise UserError("Picking Type dengan sequence_code 'OUT' tidak ditemukan.") - bu_out = create_moves( - out_type, 'OUT', - out_type.default_location_src_id.id, - self.operations.location_dest_id.id - ) - bu_out.action_confirm() + if not self.operations: + raise UserError("BU/Out harus diisi terlebih dahulu.") + + origin_so = self.operations.origin + if not origin_so: + raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") + + # Cari DO dari SO + get_group_id = self.env['stock.picking'].search([ + ('origin', '=', origin_so), + ], limit=1) + if not get_group_id: + raise UserError(f"Delivery Order dari SO {origin_so} tidak ditemukan.") + + group_id = get_group_id.group_id.id if get_group_id.group_id else False + + Picking = self.env['stock.picking'] + srt_type = self.env['stock.picking.type'].search([ + ('sequence_code', '=', 'SRT') + ], limit=1) + + ort_type = self.env['stock.picking.type'].search([ + ('sequence_code', '=', 'ORT') + ], limit=1) + + # Lokasi + location_dest_id = srt_type.default_location_dest_id.id + location_dest_id_ort = ort_type.default_location_dest_id.id + location_customer = self.operations.location_dest_id + + # 1. BU/SRT: retur dari operations + srt_picking = Picking.create({ + 'partner_id': self.operations.partner_id.id, + 'real_shipping_id': self.operations.real_shipping_id.id, + 'picking_type_id': srt_type.id, + 'location_id': location_customer.id, + 'location_dest_id': location_dest_id, + 'origin': f"Retur {self.operations.name}", + 'tukar_guling_id': self.id, + 'group_id': group_id, + 'move_ids_without_package': [ + (0, 0, { + 'name': line.name or line.product_id.name, + 'product_id': line.product_id.id, + 'product_uom_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'location_id': location_customer.id, + 'location_dest_id': location_dest_id, + 'group_id': group_id, + }) for line in self.line_ids + ] + }) + srt_picking.action_confirm() + + # 2. Cari BU/PICK dari SO yang sama + origin_so = self.operations.origin + if not origin_so: + raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") + + pick = Picking.search([ + ('origin', '=', origin_so), + ('picking_type_id.code', '=', 'internal') + ], limit=1) + + if not pick: + raise UserError(f"BU/PICK dengan origin {origin_so} tidak ditemukan.") + + # 3. BU/ORT: retur dari BU/PICK + ort_picking = Picking.create({ + 'partner_id': self.operations.partner_id.id, + 'real_shipping_id': self.operations.real_shipping_id.id, + 'picking_type_id': ort_type.id, + 'location_id': location_dest_id, + 'location_dest_id': location_dest_id_ort, + 'origin': f"Retur {pick.name}", + 'tukar_guling_id': self.id, + 'group_id': group_id, + 'move_ids_without_package': [ + (0, 0, { + 'name': line.name or line.product_id.name, + 'product_id': line.product_id.id, + 'product_uom_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'location_id': location_dest_id, + 'location_dest_id': location_dest_id_ort, + 'group_id': group_id, + }) for line in self.line_ids + ] + }) + for line in self.line_ids: + move = ort_picking.move_ids_without_package.filtered( + lambda m: m.product_id == line.product_id + )[:1] + + if move: + self.env['stock.move.line'].create({ + 'move_id': move.id, + 'picking_id': ort_picking.id, + 'product_id': line.product_id.id, + 'product_uom_id': line.product_uom.id, + 'qty_done': line.product_uom_qty, # Ambil dari return.picking.line.quantity + 'location_id': location_customer.id, + 'location_dest_id': location_dest_id, + }) + ort_picking.action_confirm() + ort_picking.action_assign() class TukarGulingLine(models.Model): _name = 'tukar.guling.line' -- cgit v1.2.3 From 41b26b7fca60533fe30240d19b972cbe7022f333 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 09:07:05 +0700 Subject: return oke --- indoteknik_custom/models/tukar_guling.py | 242 +++++++++++++++++-------------- 1 file changed, 131 insertions(+), 111 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 7bcf5e80..bdd2a2f5 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -11,6 +11,11 @@ class TukarGuling(models.Model): _order = 'date desc, id desc' _rec_name = 'name' + picking_ids = fields.One2many( + 'stock.picking', + 'tukar_guling_id', + string='Transfers') + origin = fields.Char(string='Origin SO') real_shipping_id = fields.Many2one('res.partner', string='Shipping Address') @@ -44,7 +49,16 @@ class TukarGuling(models.Model): def _onchange_operations(self): """Auto-populate lines ketika operations dipilih""" if self.operations: - # Clear existing lines + from_return_picking = self.env.context.get('from_return_picking', False) or \ + self.env.context.get('default_line_ids', False) + + if self.line_ids and from_return_picking: + # Hanya update origin, jangan ubah lines + if self.operations.origin: + self.origin = self.operations.origin + return + + # Clear existing lines hanya jika tidak dari return picking self.line_ids = [(5, 0, 0)] # Set origin dari operations @@ -91,10 +105,14 @@ class TukarGuling(models.Model): else: _logger.info("No lines created - no valid moves found") else: - # Clear lines jika operations dikosongkan - self.line_ids = [(5, 0, 0)] - self.origin = False + # Clear lines jika operations dikosongkan, kecuali dari return picking + from_return_picking = self.env.context.get('from_return_picking', False) or \ + self.env.context.get('default_line_ids', False) + if not from_return_picking: + self.line_ids = [(5, 0, 0)] + + self.origin = False def action_populate_lines(self): """Manual button untuk populate lines - sebagai alternatif""" self.ensure_one() @@ -272,113 +290,115 @@ class TukarGuling(models.Model): self.state = 'cancel' def _create_pickings(self): - if not self.operations: - raise UserError("BU/Out harus diisi terlebih dahulu.") - - origin_so = self.operations.origin - if not origin_so: - raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") - - # Cari DO dari SO - get_group_id = self.env['stock.picking'].search([ - ('origin', '=', origin_so), - ], limit=1) - - if not get_group_id: - raise UserError(f"Delivery Order dari SO {origin_so} tidak ditemukan.") - - group_id = get_group_id.group_id.id if get_group_id.group_id else False - - Picking = self.env['stock.picking'] - srt_type = self.env['stock.picking.type'].search([ - ('sequence_code', '=', 'SRT') - ], limit=1) - - ort_type = self.env['stock.picking.type'].search([ - ('sequence_code', '=', 'ORT') - ], limit=1) - - # Lokasi - location_dest_id = srt_type.default_location_dest_id.id - location_dest_id_ort = ort_type.default_location_dest_id.id - location_customer = self.operations.location_dest_id - - # 1. BU/SRT: retur dari operations - srt_picking = Picking.create({ - 'partner_id': self.operations.partner_id.id, - 'real_shipping_id': self.operations.real_shipping_id.id, - 'picking_type_id': srt_type.id, - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, - 'origin': f"Retur {self.operations.name}", - 'tukar_guling_id': self.id, - 'group_id': group_id, - 'move_ids_without_package': [ - (0, 0, { - 'name': line.name or line.product_id.name, - 'product_id': line.product_id.id, - 'product_uom_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, - 'group_id': group_id, - }) for line in self.line_ids - ] - }) - srt_picking.action_confirm() - - # 2. Cari BU/PICK dari SO yang sama - origin_so = self.operations.origin - if not origin_so: - raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") - - pick = Picking.search([ - ('origin', '=', origin_so), - ('picking_type_id.code', '=', 'internal') - ], limit=1) - - if not pick: - raise UserError(f"BU/PICK dengan origin {origin_so} tidak ditemukan.") - - # 3. BU/ORT: retur dari BU/PICK - ort_picking = Picking.create({ - 'partner_id': self.operations.partner_id.id, - 'real_shipping_id': self.operations.real_shipping_id.id, - 'picking_type_id': ort_type.id, - 'location_id': location_dest_id, - 'location_dest_id': location_dest_id_ort, - 'origin': f"Retur {pick.name}", - 'tukar_guling_id': self.id, - 'group_id': group_id, - 'move_ids_without_package': [ - (0, 0, { - 'name': line.name or line.product_id.name, - 'product_id': line.product_id.id, - 'product_uom_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'location_id': location_dest_id, - 'location_dest_id': location_dest_id_ort, - 'group_id': group_id, - }) for line in self.line_ids - ] - }) - for line in self.line_ids: - move = ort_picking.move_ids_without_package.filtered( - lambda m: m.product_id == line.product_id - )[:1] - - if move: - self.env['stock.move.line'].create({ - 'move_id': move.id, - 'picking_id': ort_picking.id, - 'product_id': line.product_id.id, - 'product_uom_id': line.product_uom.id, - 'qty_done': line.product_uom_qty, # Ambil dari return.picking.line.quantity - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, + for record in self: + if not record.operations: + raise UserError("BU/OUT dari field operations tidak ditemukan.") + + operation_picking = record.operations + + # 1. Cari semua picking DONE berdasarkan origin SO + related_pickings = self.env['stock.picking'].search([ + ('origin', '=', record.origin), + ('state', '=', 'done'), + ]) + if not related_pickings: + raise UserError("Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin + "Atau masih belum Done") + + # 2. Filter berdasarkan tipe picking + bu_pick_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 30) # BU/PICK + bu_out_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 29) # BU/OUT + + if not bu_pick_to_return and not bu_out_to_return: + raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") + + created_returns = [] + + # Lokasi default untuk retur + bu_out_type = self.env['stock.picking.type'].browse(73) + bu_stock_type = self.env['stock.picking.type'].browse(74) + + bu_out = bu_out_type.default_location_src_id.id + bu_stock = bu_out_type.default_location_dest_id.id + + if not bu_out or not bu_stock: + raise UserError("salahwoi") + + partner_location = self.env['stock.location'].browse(2) + if not partner_location: + raise UserError("Lokasi partner (real_shipping_id) tidak ditemukan pada BU/OUT utama.") + + # Fungsi membuat retur dari picking tertentu + def _create_return_from_picking(picking): + grup = self.env['stock.picking'].search([('origin', '=', self.operations.origin)]) + # Tentukan lokasi berdasarkan jenis picking + if picking.picking_type_id.id == 29: # BU/OUT → BU/SRT + default_location_id = partner_location.id + default_location_dest_id = bu_out + elif picking.picking_type_id.id == 30: # BU/PICK → BU/ORT + default_location_id = bu_out + default_location_dest_id = bu_stock + else: + return None + + return_context = dict(self.env.context) + return_context.update({ + 'active_id': picking.id, + 'default_location_id': default_location_id, + 'default_location_dest_id': default_location_dest_id, + 'from_ui': False, }) - ort_picking.action_confirm() - ort_picking.action_assign() + + return_wizard = self.env['stock.return.picking'].with_context(return_context).create({ + 'picking_id': picking.id, + 'location_id': default_location_id, + }) + + # Buat return lines + return_lines = [] + for move in picking.move_lines: + if move.quantity_done > 0: + return_lines.append((0, 0, { + 'product_id': move.product_id.id, + 'quantity': move.quantity_done, + 'move_id': move.id, + })) + if not return_lines: + return None + + return_wizard.product_return_moves = return_lines + + _logger.info("Creating return for picking %s", picking.name) + _logger.info("Default location src: %s", default_location_id) + _logger.info("Default location dest: %s", default_location_dest_id) + _logger.info("Move lines: %s", picking.move_lines) + return_vals = return_wizard.create_returns() + return_id = return_vals.get('res_id') + + if not return_id: + raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) + + picking_obj = self.env['stock.picking'].browse(return_id) + for p in picking_obj: + p.group_id = self.operations.group_id.id + p.origin_tukar_guling_id = record.id + + return picking_obj.name + + # Buat return dari BU/PICK + for picking in bu_pick_to_return: + name = _create_return_from_picking(picking) + if name: + created_returns.append(name) + + # Buat return dari BU/OUT + for picking in bu_out_to_return: + name = _create_return_from_picking(picking) + if name: + created_returns.append(name) + + if not created_returns: + raise UserError("wkwkwk") + class TukarGulingLine(models.Model): _name = 'tukar.guling.line' @@ -418,4 +438,4 @@ class TukarGulingLine(models.Model): class StockPicking(models.Model): _inherit = 'stock.picking' - origin_tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') \ No newline at end of file + tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') \ No newline at end of file -- cgit v1.2.3 From 2dcea6aa1c0aa57de8bac126f041ca547bc73cad Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 09:18:25 +0700 Subject: fix cannot validate srt --- indoteknik_custom/models/tukar_guling.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index bdd2a2f5..81ef6b1a 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -11,11 +11,6 @@ class TukarGuling(models.Model): _order = 'date desc, id desc' _rec_name = 'name' - picking_ids = fields.One2many( - 'stock.picking', - 'tukar_guling_id', - string='Transfers') - origin = fields.Char(string='Origin SO') real_shipping_id = fields.Many2one('res.partner', string='Shipping Address') @@ -323,7 +318,7 @@ class TukarGuling(models.Model): if not bu_out or not bu_stock: raise UserError("salahwoi") - partner_location = self.env['stock.location'].browse(2) + partner_location = self.env['stock.location'].browse(5) if not partner_location: raise UserError("Lokasi partner (real_shipping_id) tidak ditemukan pada BU/OUT utama.") @@ -380,7 +375,7 @@ class TukarGuling(models.Model): picking_obj = self.env['stock.picking'].browse(return_id) for p in picking_obj: p.group_id = self.operations.group_id.id - p.origin_tukar_guling_id = record.id + p.tukar_guling_id = record.id return picking_obj.name -- cgit v1.2.3 From 87bd344baa9b40cde21256bd1e3680d0d2396e2e Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 09:32:38 +0700 Subject: fix cannot validate srt --- indoteknik_custom/models/tukar_guling.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 81ef6b1a..d77ea8d4 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -108,6 +108,7 @@ class TukarGuling(models.Model): self.line_ids = [(5, 0, 0)] self.origin = False + def action_populate_lines(self): """Manual button untuk populate lines - sebagai alternatif""" self.ensure_one() @@ -215,6 +216,7 @@ class TukarGuling(models.Model): line.sequence = (i + 1) * 10 return new_record + def write(self, vals): if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) @@ -297,7 +299,8 @@ class TukarGuling(models.Model): ('state', '=', 'done'), ]) if not related_pickings: - raise UserError("Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin + "Atau masih belum Done") + raise UserError( + "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin + "Atau masih belum Done") # 2. Filter berdasarkan tipe picking bu_pick_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 30) # BU/PICK @@ -318,9 +321,11 @@ class TukarGuling(models.Model): if not bu_out or not bu_stock: raise UserError("salahwoi") - partner_location = self.env['stock.location'].browse(5) + partner_location = self.env['stock.location'].search( + [('complete_name', 'ilike', 'Partner Locations/Customers'), + ('id', '=', '5')]) if not partner_location: - raise UserError("Lokasi partner (real_shipping_id) tidak ditemukan pada BU/OUT utama.") + raise UserError("Lokasi partner salah atau tidak ditemukan pada BU/OUT.") # Fungsi membuat retur dari picking tertentu def _create_return_from_picking(picking): @@ -433,4 +438,4 @@ class TukarGulingLine(models.Model): class StockPicking(models.Model): _inherit = 'stock.picking' - tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') \ No newline at end of file + tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') -- cgit v1.2.3 From c1fa178a6afb9ef4a914dc617d2fb69da50af673 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 11:21:52 +0700 Subject: fix from and to return --- indoteknik_custom/models/tukar_guling.py | 56 +++++++++++++++++++------------- 1 file changed, 34 insertions(+), 22 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index d77ea8d4..1f5e786f 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -300,11 +300,11 @@ class TukarGuling(models.Model): ]) if not related_pickings: raise UserError( - "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin + "Atau masih belum Done") + "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin) # 2. Filter berdasarkan tipe picking - bu_pick_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 30) # BU/PICK - bu_out_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 29) # BU/OUT + bu_pick_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 30) # BU/PICK + bu_out_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 29) # BU/OUT if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") @@ -312,13 +312,18 @@ class TukarGuling(models.Model): created_returns = [] # Lokasi default untuk retur - bu_out_type = self.env['stock.picking.type'].browse(73) - bu_stock_type = self.env['stock.picking.type'].browse(74) + srt_type = self.env['stock.picking.type'].browse(73) + ort_type = self.env['stock.picking.type'].browse(74) - bu_out = bu_out_type.default_location_src_id.id - bu_stock = bu_out_type.default_location_dest_id.id + stock_location = self.env['stock.location'] - if not bu_out or not bu_stock: + srt_src = stock_location.browse(5) + srt_dest = stock_location.browse(60) + + ort_src = stock_location.browse(60) + ort_dest = stock_location.browse(57) + + if not ort_src or not ort_dest or not srt_src or not srt_dest: raise UserError("salahwoi") partner_location = self.env['stock.location'].search( @@ -329,14 +334,19 @@ class TukarGuling(models.Model): # Fungsi membuat retur dari picking tertentu def _create_return_from_picking(picking): - grup = self.env['stock.picking'].search([('origin', '=', self.operations.origin)]) - # Tentukan lokasi berdasarkan jenis picking + grup = self.operations.group_id + + PARTNER_LOCATION_ID = 5 # Partner Locations/Customers + BU_OUTPUT_LOCATION_ID = 60 # BU/Output (from your logs) + BU_STOCK_LOCATION_ID = 57 # BU/Stock (adjust to your actual ID) + + # Determine locations based on picking type if picking.picking_type_id.id == 29: # BU/OUT → BU/SRT - default_location_id = partner_location.id - default_location_dest_id = bu_out + default_location_id = PARTNER_LOCATION_ID # From: Partner Locations + default_location_dest_id = BU_OUTPUT_LOCATION_ID # To: BU/Output elif picking.picking_type_id.id == 30: # BU/PICK → BU/ORT - default_location_id = bu_out - default_location_dest_id = bu_stock + default_location_id = BU_OUTPUT_LOCATION_ID # From: BU/Output + default_location_dest_id = BU_STOCK_LOCATION_ID # To: BU/Stock (FIXED) else: return None @@ -353,7 +363,7 @@ class TukarGuling(models.Model): 'location_id': default_location_id, }) - # Buat return lines + # Create return lines return_lines = [] for move in picking.move_lines: if move.quantity_done > 0: @@ -370,19 +380,21 @@ class TukarGuling(models.Model): _logger.info("Creating return for picking %s", picking.name) _logger.info("Default location src: %s", default_location_id) _logger.info("Default location dest: %s", default_location_dest_id) - _logger.info("Move lines: %s", picking.move_lines) return_vals = return_wizard.create_returns() return_id = return_vals.get('res_id') + return_picking = self.env['stock.picking'].browse(return_id) - if not return_id: + if not return_picking: raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) - picking_obj = self.env['stock.picking'].browse(return_id) - for p in picking_obj: - p.group_id = self.operations.group_id.id - p.tukar_guling_id = record.id + # Force the destination location (extra safeguard) + return_picking.write({ + 'location_dest_id': default_location_dest_id, + 'group_id': grup.id, + 'tukar_guling_id': record.id, + }) - return picking_obj.name + return return_picking.name # Buat return dari BU/PICK for picking in bu_pick_to_return: -- cgit v1.2.3 From 19c7a29333bd2c196a4aec2b173293da4d25e3ab Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 11:38:17 +0700 Subject: fix product not showing in detailed operations --- indoteknik_custom/models/tukar_guling.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 1f5e786f..740bb7d7 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -366,10 +366,11 @@ class TukarGuling(models.Model): # Create return lines return_lines = [] for move in picking.move_lines: - if move.quantity_done > 0: + qty = move.quantity_done or move.product_uom_qty + if qty > 0: return_lines.append((0, 0, { 'product_id': move.product_id.id, - 'quantity': move.quantity_done, + 'quantity': qty, 'move_id': move.id, })) if not return_lines: -- cgit v1.2.3 From cbf88d93e082f30305123deb467c4c15916d9519 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 15:05:43 +0700 Subject: Revert " fix from and to return" wkwkwk This reverts commit c1fa178a6afb9ef4a914dc617d2fb69da50af673. --- indoteknik_custom/models/tukar_guling.py | 56 +++++++++++++------------------- 1 file changed, 22 insertions(+), 34 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 740bb7d7..ff52bab5 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -300,11 +300,11 @@ class TukarGuling(models.Model): ]) if not related_pickings: raise UserError( - "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin) + "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin + "Atau masih belum Done") # 2. Filter berdasarkan tipe picking - bu_pick_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 30) # BU/PICK - bu_out_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 29) # BU/OUT + bu_pick_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 30) # BU/PICK + bu_out_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 29) # BU/OUT if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") @@ -312,18 +312,13 @@ class TukarGuling(models.Model): created_returns = [] # Lokasi default untuk retur - srt_type = self.env['stock.picking.type'].browse(73) - ort_type = self.env['stock.picking.type'].browse(74) + bu_out_type = self.env['stock.picking.type'].browse(73) + bu_stock_type = self.env['stock.picking.type'].browse(74) - stock_location = self.env['stock.location'] + bu_out = bu_out_type.default_location_src_id.id + bu_stock = bu_out_type.default_location_dest_id.id - srt_src = stock_location.browse(5) - srt_dest = stock_location.browse(60) - - ort_src = stock_location.browse(60) - ort_dest = stock_location.browse(57) - - if not ort_src or not ort_dest or not srt_src or not srt_dest: + if not bu_out or not bu_stock: raise UserError("salahwoi") partner_location = self.env['stock.location'].search( @@ -334,19 +329,14 @@ class TukarGuling(models.Model): # Fungsi membuat retur dari picking tertentu def _create_return_from_picking(picking): - grup = self.operations.group_id - - PARTNER_LOCATION_ID = 5 # Partner Locations/Customers - BU_OUTPUT_LOCATION_ID = 60 # BU/Output (from your logs) - BU_STOCK_LOCATION_ID = 57 # BU/Stock (adjust to your actual ID) - - # Determine locations based on picking type + grup = self.env['stock.picking'].search([('origin', '=', self.operations.origin)]) + # Tentukan lokasi berdasarkan jenis picking if picking.picking_type_id.id == 29: # BU/OUT → BU/SRT - default_location_id = PARTNER_LOCATION_ID # From: Partner Locations - default_location_dest_id = BU_OUTPUT_LOCATION_ID # To: BU/Output + default_location_id = partner_location.id + default_location_dest_id = bu_out elif picking.picking_type_id.id == 30: # BU/PICK → BU/ORT - default_location_id = BU_OUTPUT_LOCATION_ID # From: BU/Output - default_location_dest_id = BU_STOCK_LOCATION_ID # To: BU/Stock (FIXED) + default_location_id = bu_out + default_location_dest_id = bu_stock else: return None @@ -363,7 +353,7 @@ class TukarGuling(models.Model): 'location_id': default_location_id, }) - # Create return lines + # Buat return lines return_lines = [] for move in picking.move_lines: qty = move.quantity_done or move.product_uom_qty @@ -381,21 +371,19 @@ class TukarGuling(models.Model): _logger.info("Creating return for picking %s", picking.name) _logger.info("Default location src: %s", default_location_id) _logger.info("Default location dest: %s", default_location_dest_id) + _logger.info("Move lines: %s", picking.move_lines) return_vals = return_wizard.create_returns() return_id = return_vals.get('res_id') - return_picking = self.env['stock.picking'].browse(return_id) - if not return_picking: + if not return_id: raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) - # Force the destination location (extra safeguard) - return_picking.write({ - 'location_dest_id': default_location_dest_id, - 'group_id': grup.id, - 'tukar_guling_id': record.id, - }) + picking_obj = self.env['stock.picking'].browse(return_id) + for p in picking_obj: + p.group_id = self.operations.group_id.id + p.tukar_guling_id = record.id - return return_picking.name + return picking_obj.name # Buat return dari BU/PICK for picking in bu_pick_to_return: -- cgit v1.2.3 From ba4b74c9dc301dec73217bb6c35bb78ab7f41fb3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 15:29:50 +0700 Subject: re fix location --- indoteknik_custom/models/tukar_guling.py | 61 +++++++++++++++++++------------- 1 file changed, 37 insertions(+), 24 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index ff52bab5..da1cfcf4 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -1,4 +1,4 @@ -from odoo import models, fields, api +from odoo import models, fields, api, _ from odoo.exceptions import UserError, ValidationError import logging @@ -20,6 +20,7 @@ class TukarGuling(models.Model): 'tukar_guling_id', string='Transfers' ) + # origin_so = fields.Many2one('sale.order', string='Origin SO') name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) operations = fields.Many2one('stock.picking', 'Operations', @@ -300,11 +301,11 @@ class TukarGuling(models.Model): ]) if not related_pickings: raise UserError( - "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin + "Atau masih belum Done") + "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin) # 2. Filter berdasarkan tipe picking - bu_pick_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 30) # BU/PICK - bu_out_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 29) # BU/OUT + bu_pick_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 30) # BU/PICK + bu_out_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 29) # BU/OUT if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") @@ -312,13 +313,18 @@ class TukarGuling(models.Model): created_returns = [] # Lokasi default untuk retur - bu_out_type = self.env['stock.picking.type'].browse(73) - bu_stock_type = self.env['stock.picking.type'].browse(74) + srt_type = self.env['stock.picking.type'].browse(73) + ort_type = self.env['stock.picking.type'].browse(74) - bu_out = bu_out_type.default_location_src_id.id - bu_stock = bu_out_type.default_location_dest_id.id + stock_location = self.env['stock.location'] - if not bu_out or not bu_stock: + srt_src = stock_location.browse(5) + srt_dest = stock_location.browse(60) + + ort_src = stock_location.browse(60) + ort_dest = stock_location.browse(57) + + if not ort_src or not ort_dest or not srt_src or not srt_dest: raise UserError("salahwoi") partner_location = self.env['stock.location'].search( @@ -329,14 +335,19 @@ class TukarGuling(models.Model): # Fungsi membuat retur dari picking tertentu def _create_return_from_picking(picking): - grup = self.env['stock.picking'].search([('origin', '=', self.operations.origin)]) - # Tentukan lokasi berdasarkan jenis picking + grup = self.operations.group_id + + PARTNER_LOCATION_ID = 5 # Partner Locations/Customers + BU_OUTPUT_LOCATION_ID = 60 # BU/Output (from your logs) + BU_STOCK_LOCATION_ID = 57 # BU/Stock (adjust to your actual ID) + + # Determine locations based on picking type if picking.picking_type_id.id == 29: # BU/OUT → BU/SRT - default_location_id = partner_location.id - default_location_dest_id = bu_out + default_location_id = PARTNER_LOCATION_ID # From: Partner Locations + default_location_dest_id = BU_OUTPUT_LOCATION_ID # To: BU/Output elif picking.picking_type_id.id == 30: # BU/PICK → BU/ORT - default_location_id = bu_out - default_location_dest_id = bu_stock + default_location_id = BU_OUTPUT_LOCATION_ID # From: BU/Output + default_location_dest_id = BU_STOCK_LOCATION_ID # To: BU/Stock (FIXED) else: return None @@ -353,7 +364,7 @@ class TukarGuling(models.Model): 'location_id': default_location_id, }) - # Buat return lines + # Create return lines return_lines = [] for move in picking.move_lines: qty = move.quantity_done or move.product_uom_qty @@ -371,19 +382,21 @@ class TukarGuling(models.Model): _logger.info("Creating return for picking %s", picking.name) _logger.info("Default location src: %s", default_location_id) _logger.info("Default location dest: %s", default_location_dest_id) - _logger.info("Move lines: %s", picking.move_lines) return_vals = return_wizard.create_returns() return_id = return_vals.get('res_id') + return_picking = self.env['stock.picking'].browse(return_id) - if not return_id: + if not return_picking: raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) - picking_obj = self.env['stock.picking'].browse(return_id) - for p in picking_obj: - p.group_id = self.operations.group_id.id - p.tukar_guling_id = record.id + # Force the destination location (extra safeguard) + return_picking.write({ + 'location_dest_id': default_location_dest_id, + 'group_id': grup.id, + 'tukar_guling_id': record.id, + }) - return picking_obj.name + return return_picking.name # Buat return dari BU/PICK for picking in bu_pick_to_return: @@ -439,4 +452,4 @@ class TukarGulingLine(models.Model): class StockPicking(models.Model): _inherit = 'stock.picking' - tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') + tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') \ No newline at end of file -- cgit v1.2.3 From 1be3cacacce54b6fe71eb3786d152c1d18707724 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 16:07:48 +0700 Subject: re fix location --- indoteknik_custom/models/tukar_guling.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index da1cfcf4..5c99bc18 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -337,17 +337,17 @@ class TukarGuling(models.Model): def _create_return_from_picking(picking): grup = self.operations.group_id - PARTNER_LOCATION_ID = 5 # Partner Locations/Customers - BU_OUTPUT_LOCATION_ID = 60 # BU/Output (from your logs) - BU_STOCK_LOCATION_ID = 57 # BU/Stock (adjust to your actual ID) + PARTNER_LOCATION_ID = 5 + BU_OUTPUT_LOCATION_ID = 60 + BU_STOCK_LOCATION_ID = 57 # Determine locations based on picking type - if picking.picking_type_id.id == 29: # BU/OUT → BU/SRT - default_location_id = PARTNER_LOCATION_ID # From: Partner Locations - default_location_dest_id = BU_OUTPUT_LOCATION_ID # To: BU/Output - elif picking.picking_type_id.id == 30: # BU/PICK → BU/ORT - default_location_id = BU_OUTPUT_LOCATION_ID # From: BU/Output - default_location_dest_id = BU_STOCK_LOCATION_ID # To: BU/Stock (FIXED) + if picking.picking_type_id.id == 29: + default_location_id = PARTNER_LOCATION_ID + default_location_dest_id = BU_OUTPUT_LOCATION_ID + elif picking.picking_type_id.id == 30: + default_location_id = BU_OUTPUT_LOCATION_ID + default_location_dest_id = BU_STOCK_LOCATION_ID else: return None @@ -389,7 +389,7 @@ class TukarGuling(models.Model): if not return_picking: raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) - # Force the destination location (extra safeguard) + # Force the destination location return_picking.write({ 'location_dest_id': default_location_dest_id, 'group_id': grup.id, -- cgit v1.2.3 From 680748ae128a90b9999acff60c770e2472c7fcbe Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 21 Jun 2025 20:52:32 +0700 Subject: it should be done --- indoteknik_custom/models/tukar_guling.py | 37 +++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 5c99bc18..a7b6e07e 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -315,6 +315,8 @@ class TukarGuling(models.Model): # Lokasi default untuk retur srt_type = self.env['stock.picking.type'].browse(73) ort_type = self.env['stock.picking.type'].browse(74) + bu_pick_type = self.env['stock.picking.type'].browse(30) + bu_out_type = self.env['stock.picking.type'].browse(29) stock_location = self.env['stock.location'] @@ -342,12 +344,22 @@ class TukarGuling(models.Model): BU_STOCK_LOCATION_ID = 57 # Determine locations based on picking type - if picking.picking_type_id.id == 29: + if picking.picking_type_id.id == 30: # -> ngeretur bu pick + return_type = srt_type + default_location_id = BU_OUTPUT_LOCATION_ID + default_location_dest_id = BU_STOCK_LOCATION_ID + elif picking.picking_type_id.id == 29: # -> ngeretur bu out + return_type = ort_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID - elif picking.picking_type_id.id == 30: + elif picking.picking_type_id.id == 74: # -> ngeretur srt + return_type = bu_pick_type + default_location_id = BU_STOCK_LOCATION_ID + default_location_dest_id = BU_OUTPUT_LOCATION_ID + elif picking.picking_type_id.id == 73: # -> ngeretur ort + return_type = bu_out_type default_location_id = BU_OUTPUT_LOCATION_ID - default_location_dest_id = BU_STOCK_LOCATION_ID + default_location_dest_id = PARTNER_LOCATION_ID else: return None @@ -396,20 +408,29 @@ class TukarGuling(models.Model): 'tukar_guling_id': record.id, }) - return return_picking.name + return return_picking - # Buat return dari BU/PICK - for picking in bu_pick_to_return: + # Buat return dari BU/OUT + for picking in bu_out_to_return: name = _create_return_from_picking(picking) if name: created_returns.append(name) - # Buat return dari BU/OUT - for picking in bu_out_to_return: + # Buat return dari BU/PICK + for picking in bu_pick_to_return: name = _create_return_from_picking(picking) if name: created_returns.append(name) + # Buat return dari SRT + if record.return_type == 'tukar_guling': + target = [woi for woi in created_returns if woi.picking_type_id.id in (73, 74)] + for picking in target: + retur = _create_return_from_picking(picking) + if retur: + created_returns.append(retur) + + if not created_returns: raise UserError("wkwkwk") -- cgit v1.2.3 From 39e570854fa72d673bd37fc6582861bf1fc49aa7 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sun, 22 Jun 2025 21:09:36 +0700 Subject: should be done --- indoteknik_custom/models/tukar_guling.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index a7b6e07e..456a2111 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -303,7 +303,7 @@ class TukarGuling(models.Model): raise UserError( "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin) - # 2. Filter berdasarkan tipe picking + # filter based on stockin.picking picking type bu_pick_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 30) # BU/PICK bu_out_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 29) # BU/OUT @@ -345,11 +345,11 @@ class TukarGuling(models.Model): # Determine locations based on picking type if picking.picking_type_id.id == 30: # -> ngeretur bu pick - return_type = srt_type + return_type = ort_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID elif picking.picking_type_id.id == 29: # -> ngeretur bu out - return_type = ort_type + return_type = srt_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID elif picking.picking_type_id.id == 74: # -> ngeretur srt @@ -373,7 +373,8 @@ class TukarGuling(models.Model): return_wizard = self.env['stock.return.picking'].with_context(return_context).create({ 'picking_id': picking.id, - 'location_id': default_location_id, + 'location_id': default_location_dest_id, + 'original_location_id': default_location_id }) # Create return lines @@ -404,6 +405,7 @@ class TukarGuling(models.Model): # Force the destination location return_picking.write({ 'location_dest_id': default_location_dest_id, + 'location_id': default_location_id, 'group_id': grup.id, 'tukar_guling_id': record.id, }) @@ -422,9 +424,9 @@ class TukarGuling(models.Model): if name: created_returns.append(name) - # Buat return dari SRT + # Buat return dari SRT dan ort if record.return_type == 'tukar_guling': - target = [woi for woi in created_returns if woi.picking_type_id.id in (73, 74)] + target = [woi for woi in created_returns if woi.picking_type_id.id in (74, 73)] for picking in target: retur = _create_return_from_picking(picking) if retur: -- cgit v1.2.3 From f0f3e9d142dd435c9740f6d6c439e1b400a36c45 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 23 Jun 2025 15:28:01 +0700 Subject: fix qty --- indoteknik_custom/models/tukar_guling.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 456a2111..dfa62dc8 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -202,7 +202,7 @@ class TukarGuling(models.Model): if sequence: default['name'] = sequence.next_by_id() else: - default['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'PTG-COPY' + default['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'copy' default.update({ 'state': 'draft', @@ -312,6 +312,7 @@ class TukarGuling(models.Model): created_returns = [] + # Lokasi default untuk retur srt_type = self.env['stock.picking.type'].browse(73) ort_type = self.env['stock.picking.type'].browse(74) @@ -329,12 +330,6 @@ class TukarGuling(models.Model): if not ort_src or not ort_dest or not srt_src or not srt_dest: raise UserError("salahwoi") - partner_location = self.env['stock.location'].search( - [('complete_name', 'ilike', 'Partner Locations/Customers'), - ('id', '=', '5')]) - if not partner_location: - raise UserError("Lokasi partner salah atau tidak ditemukan pada BU/OUT.") - # Fungsi membuat retur dari picking tertentu def _create_return_from_picking(picking): grup = self.operations.group_id @@ -348,19 +343,28 @@ class TukarGuling(models.Model): return_type = ort_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID + if not default_location_id or not default_location_dest_id: + raise UserError("Lokasi Origin atau Destination salah.") elif picking.picking_type_id.id == 29: # -> ngeretur bu out return_type = srt_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID + if not default_location_id or not default_location_dest_id: + raise UserError("Lokasi Origin atau Destination salah.") elif picking.picking_type_id.id == 74: # -> ngeretur srt return_type = bu_pick_type default_location_id = BU_STOCK_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID + if not default_location_id or not default_location_dest_id: + raise UserError("Lokasi Origin atau Destination salah.") elif picking.picking_type_id.id == 73: # -> ngeretur ort return_type = bu_out_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID + if not default_location_id or not default_location_dest_id: + raise UserError("Lokasi Origin atau Destination salah.") else: + raise UserError("Hayo") return None return_context = dict(self.env.context) @@ -379,12 +383,12 @@ class TukarGuling(models.Model): # Create return lines return_lines = [] - for move in picking.move_lines: - qty = move.quantity_done or move.product_uom_qty - if qty > 0: + for line in record.line_ids: + move = picking.move_lines.filtered(lambda wkwk: wkwk.product_id == line.product_id) + if move: return_lines.append((0, 0, { - 'product_id': move.product_id.id, - 'quantity': qty, + 'product_id': line.product_id.id, + 'quantity': line.product_uom_qty, 'move_id': move.id, })) if not return_lines: -- cgit v1.2.3 From 0d95dabc04a3f5334168e989705e9a7568130df4 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 24 Jun 2025 08:41:47 +0700 Subject: spesific tf --- indoteknik_custom/models/tukar_guling.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index dfa62dc8..d75dad41 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -25,6 +25,7 @@ class TukarGuling(models.Model): date = fields.Datetime('Date', default=fields.Datetime.now, required=True) operations = fields.Many2one('stock.picking', 'Operations', domain=[('picking_type_id.code', '=', 'outgoing')], help='Nomor BU/Out atau BU/Pick') + spesific_operations = fields.Many2one('stock.picking', 'Spesific Operations', domain = [('origin', '=', origin), ('state', '=', 'done')]) ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ @@ -44,6 +45,10 @@ class TukarGuling(models.Model): @api.onchange('operations') def _onchange_operations(self): """Auto-populate lines ketika operations dipilih""" + if self.origin: + return {'domain': { + 'specific_transfers': [('origin', '=', self.origin), ('state', '=', 'done')] + }} if self.operations: from_return_picking = self.env.context.get('from_return_picking', False) or \ self.env.context.get('default_line_ids', False) @@ -294,7 +299,6 @@ class TukarGuling(models.Model): operation_picking = record.operations - # 1. Cari semua picking DONE berdasarkan origin SO related_pickings = self.env['stock.picking'].search([ ('origin', '=', record.origin), ('state', '=', 'done'), @@ -391,6 +395,8 @@ class TukarGuling(models.Model): 'quantity': line.product_uom_qty, 'move_id': move.id, })) + if not move: + raise UserError("eror woi") if not return_lines: return None -- cgit v1.2.3 From 8fc5488d18ac3005df9e8e259b2fbc0b67664841 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 25 Jun 2025 04:58:10 +0700 Subject: additional changes --- indoteknik_custom/models/tukar_guling.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index d75dad41..0c6e5eca 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -302,14 +302,15 @@ class TukarGuling(models.Model): related_pickings = self.env['stock.picking'].search([ ('origin', '=', record.origin), ('state', '=', 'done'), + ('picking_type_id', 'in', [29, 30]) ]) if not related_pickings: raise UserError( "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin) # filter based on stockin.picking picking type - bu_pick_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 30) # BU/PICK - bu_out_to_return = related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 29) # BU/OUT + bu_pick_to_return = self.env['stock.picking'] or related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 30) # BU/PICK + bu_out_to_return = record.operations or related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 29) # BU/OUT if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") -- cgit v1.2.3 From f8765bb7a9b6095c3b79d71f65ce8ae4a041da6d Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 25 Jun 2025 08:04:39 +0700 Subject: don --- indoteknik_custom/models/tukar_guling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 0c6e5eca..e00c21f8 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -309,8 +309,8 @@ class TukarGuling(models.Model): "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin) # filter based on stockin.picking picking type - bu_pick_to_return = self.env['stock.picking'] or related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 30) # BU/PICK - bu_out_to_return = record.operations or related_pickings.filtered(lambda ktl: ktl.picking_type_id.id == 29) # BU/OUT + bu_pick_to_return = record.operations.konfirm_koli_lines.pick_id + bu_out_to_return = record.operations if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") -- cgit v1.2.3 From 3b6b3a2f7a161a08fa3995906f92019cc5ea4d17 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 26 Jun 2025 10:32:15 +0700 Subject: don --- indoteknik_custom/models/tukar_guling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index e00c21f8..681090f0 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -308,7 +308,7 @@ class TukarGuling(models.Model): raise UserError( "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin) - # filter based on stockin.picking picking type + # filter based on stock.picking picking type bu_pick_to_return = record.operations.konfirm_koli_lines.pick_id bu_out_to_return = record.operations -- cgit v1.2.3 From 7ac0e952f1ce4be4bbe3c374a646a7b106746581 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 26 Jun 2025 15:06:59 +0700 Subject: jago --- indoteknik_custom/models/tukar_guling.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 681090f0..77538c91 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -24,8 +24,8 @@ class TukarGuling(models.Model): name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) operations = fields.Many2one('stock.picking', 'Operations', - domain=[('picking_type_id.code', '=', 'outgoing')], help='Nomor BU/Out atau BU/Pick') - spesific_operations = fields.Many2one('stock.picking', 'Spesific Operations', domain = [('origin', '=', origin), ('state', '=', 'done')]) + domain=[('picking_type_id.sequence_code', 'in', ['OUT', 'PICK']), + ('state', '=', 'done')], help='Nomor BU/Out atau BU/Pick') ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ @@ -45,10 +45,6 @@ class TukarGuling(models.Model): @api.onchange('operations') def _onchange_operations(self): """Auto-populate lines ketika operations dipilih""" - if self.origin: - return {'domain': { - 'specific_transfers': [('origin', '=', self.origin), ('state', '=', 'done')] - }} if self.operations: from_return_picking = self.env.context.get('from_return_picking', False) or \ self.env.context.get('default_line_ids', False) -- cgit v1.2.3 From 1e01a6845781821fcf65a42c4528b3efe144e28c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 26 Jun 2025 15:23:24 +0700 Subject: show bu pick where the bu out is not done --- indoteknik_custom/models/tukar_guling.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 77538c91..6fbca20c 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -24,8 +24,10 @@ class TukarGuling(models.Model): name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) operations = fields.Many2one('stock.picking', 'Operations', - domain=[('picking_type_id.sequence_code', 'in', ['OUT', 'PICK']), - ('state', '=', 'done')], help='Nomor BU/Out atau BU/Pick') + domain=[('picking_type_id.sequence_code', '=', 'OUT'), + ('state', '=', 'done'), '&', + ('picking_type_id.sequence_code', '=', 'PICK'), + ('state', '!=', 'done')], help='Nomor BU/Out atau BU/Pick') ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ @@ -76,11 +78,6 @@ class TukarGuling(models.Model): elif hasattr(self.operations, 'move_lines') and self.operations.move_lines: moves_to_check = self.operations.move_lines - # Debug logging - _logger = logging.getLogger(__name__) - _logger.info(f"BU/OUT: {self.operations.name}, State: {self.operations.state}") - _logger.info(f"Total moves found: {len(moves_to_check)}") - for move in moves_to_check: _logger.info( f"Move: {move.name}, Product: {move.product_id.name if move.product_id else 'No Product'}, Qty: {move.product_uom_qty}, State: {move.state}") @@ -115,7 +112,7 @@ class TukarGuling(models.Model): """Manual button untuk populate lines - sebagai alternatif""" self.ensure_one() if not self.operations: - raise UserError("Pilih BU/OUT terlebih dahulu!") + raise UserError("Pilih BU/OUT atau BU/PICK terlebih dahulu!") # Clear existing lines self.line_ids = [(5, 0, 0)] @@ -144,7 +141,7 @@ class TukarGuling(models.Model): def _check_required_bu_fields(self): for record in self: if record.return_type in ['revisi_so', 'tukar_guling'] and not record.operations: - raise ValidationError("BU/Out harus diisi!") + raise ValidationError("Operations harus diisi") @api.constrains('line_ids', 'state') def _check_product_lines(self): @@ -178,7 +175,6 @@ class TukarGuling(models.Model): def create(self, vals): # Generate sequence number if not vals.get('name') or vals['name'] == 'New': - # Pastikan sequence code 'tukar.guling' ada sequence = self.env['ir.sequence'].search([('code', '=', 'tukar.guling')], limit=1) if sequence: vals['name'] = sequence.next_by_id() @@ -257,7 +253,7 @@ class TukarGuling(models.Model): self.ensure_one() if not self.operations: - raise UserError("BU/Out harus diisi!") + raise UserError("Operations harus diisi!") if not self.return_type: raise UserError("Return Type harus diisi!") -- cgit v1.2.3 From b30cbc06bb4075b6c1fdab056cb5f5ea8d077d6c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 26 Jun 2025 15:28:21 +0700 Subject: show bu pick where the bu out is not done --- indoteknik_custom/models/tukar_guling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 6fbca20c..6b111029 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -27,7 +27,7 @@ class TukarGuling(models.Model): domain=[('picking_type_id.sequence_code', '=', 'OUT'), ('state', '=', 'done'), '&', ('picking_type_id.sequence_code', '=', 'PICK'), - ('state', '!=', 'done')], help='Nomor BU/Out atau BU/Pick') + ('linked_manual_bu_out.state', '!=', 'done')], help='Nomor BU/Out atau BU/Pick') ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ -- cgit v1.2.3 From 69eed6936f7749809ee57bc4fa4f408fc93da6d3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 26 Jun 2025 15:30:17 +0700 Subject: show bu pick --- indoteknik_custom/models/tukar_guling.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 6b111029..c1bd82bb 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -24,10 +24,8 @@ class TukarGuling(models.Model): name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) operations = fields.Many2one('stock.picking', 'Operations', - domain=[('picking_type_id.sequence_code', '=', 'OUT'), - ('state', '=', 'done'), '&', - ('picking_type_id.sequence_code', '=', 'PICK'), - ('linked_manual_bu_out.state', '!=', 'done')], help='Nomor BU/Out atau BU/Pick') + domain=[('picking_type_id.sequence_code', 'in', 'OUT'), + ('state', '=', 'done')], help='Nomor BU/Out atau BU/Pick') ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ -- cgit v1.2.3 From bc99ea265c50e22d0a6cd74e1e5a4d5f27988701 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 26 Jun 2025 16:59:47 +0700 Subject: create ort only when retur from bu pick --- indoteknik_custom/models/tukar_guling.py | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index c1bd82bb..601603d7 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -24,8 +24,8 @@ class TukarGuling(models.Model): name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) operations = fields.Many2one('stock.picking', 'Operations', - domain=[('picking_type_id.sequence_code', 'in', 'OUT'), - ('state', '=', 'done')], help='Nomor BU/Out atau BU/Pick') + domain=[('picking_type_id.sequence_code', 'in', ['OUT', 'PICK']), + ('state', '=', 'done'), ('linked_manual_bu_out', '!=', 'done')], help='Nomor BU/Out atau BU/Pick') ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ @@ -287,8 +287,6 @@ class TukarGuling(models.Model): if not record.operations: raise UserError("BU/OUT dari field operations tidak ditemukan.") - operation_picking = record.operations - related_pickings = self.env['stock.picking'].search([ ('origin', '=', record.origin), ('state', '=', 'done'), @@ -414,24 +412,26 @@ class TukarGuling(models.Model): return return_picking # Buat return dari BU/OUT - for picking in bu_out_to_return: - name = _create_return_from_picking(picking) - if name: - created_returns.append(name) + if record.operations.picking_type_id == 30: + for picking in [bu_pick_to_return]: + name = _create_return_from_picking(picking) + if name: + created_returns.append(name) # Buat return dari BU/PICK - for picking in bu_pick_to_return: - name = _create_return_from_picking(picking) - if name: - created_returns.append(name) - - # Buat return dari SRT dan ort - if record.return_type == 'tukar_guling': - target = [woi for woi in created_returns if woi.picking_type_id.id in (74, 73)] - for picking in target: - retur = _create_return_from_picking(picking) - if retur: - created_returns.append(retur) + else: + for picking in [bu_out_to_return]: + name = _create_return_from_picking(picking) + if name: + created_returns.append(name) + + # Buat return dari SRT dan ort + if record.return_type == 'tukar_guling' and record.operations.picking_type_id.id != 30: + target = [woi for woi in created_returns if woi.picking_type_id.id in (74, 73)] + for picking in target: + retur = _create_return_from_picking(picking) + if retur: + created_returns.append(retur) if not created_returns: -- cgit v1.2.3 From 2c3358216c4a2b09ef0df3a2f8998889198ad310 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 26 Jun 2025 21:33:44 +0700 Subject: add validations --- indoteknik_custom/models/tukar_guling.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 601603d7..e1513c2e 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -243,8 +243,30 @@ class TukarGuling(models.Model): def action_submit(self): self.ensure_one() + picking = self.operations + if self.state != 'draft': raise UserError("Submit hanya bisa dilakukan dari Draft.") + + if not self.operations: + raise UserError("Operations harus diisi!") + + if not self.return_type: + raise UserError("Return Type harus diisi!") + + self._validate_product_lines() + + if picking.picking_type_id.sequence_code == 'OUT' or picking.picking_type_id.id == 29: + if picking.state != 'done': + raise UserError("BU/OUT Harus Sudah DONE") + + elif picking.picking_type_id.sequence_code == 'PICK' or picking.picking_type_id.id == 30: + linked_bu_out = picking.linked_manual_bu_out + if linked_bu_out.state == 'done': + raise UserError("BU/PICK yang bisa diretur ketika BU/OUT yang belum DONE") + else: + raise UserError("Picking Type harus BU/OUT atau BU/PICK") + self.state = 'approval_sales' def action_approve(self): -- cgit v1.2.3 From 605385c9c0bf3ed95ada1628c02f00b53dc19eb6 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 27 Jun 2025 15:45:45 +0700 Subject: validation rev --- indoteknik_custom/models/tukar_guling.py | 112 +++++++++++++++++-------------- 1 file changed, 63 insertions(+), 49 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index e1513c2e..df501261 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -23,9 +23,23 @@ class TukarGuling(models.Model): # origin_so = fields.Many2one('sale.order', string='Origin SO') name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) - operations = fields.Many2one('stock.picking', 'Operations', - domain=[('picking_type_id.sequence_code', 'in', ['OUT', 'PICK']), - ('state', '=', 'done'), ('linked_manual_bu_out', '!=', 'done')], help='Nomor BU/Out atau BU/Pick') + operations = fields.Many2one( + 'stock.picking', + string='Operations', + domain=[ + '|', + # BU/OUT + '&', + ('picking_type_id.id', '=', 29), + ('state', '=', 'done'), + '&', + '&', + ('picking_type_id.id', '=', 30), + ('state', '=', 'done'), + ('linked_manual_bu_out', '!=', 'done'), + ], + help='Nomor BU/OUT atau BU/PICK' + ) ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ @@ -169,6 +183,12 @@ class TukarGuling(models.Model): return True + def _is_already_returned(self, picking): + return self.env['stock.picking'].search_count([ + ('origin', '=', 'Return of %s' % picking.name), + ('state', '!=', 'cancel') + ]) > 0 + @api.model def create(self, vals): # Generate sequence number @@ -214,6 +234,8 @@ class TukarGuling(models.Model): return new_record def write(self, vals): + if self._is_already_returned(self.operations): + raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: @@ -242,35 +264,29 @@ class TukarGuling(models.Model): def action_submit(self): self.ensure_one() + picking = self.env['stock.picking'] + if picking.picking_type_id.id == 29: + if picking.picking_type_id.state != 'done': + raise UserError("BU/OUT belum Done!") + elif picking.picking_type_id.id == 30: + linked_bu_out = picking.linked_manual_bu_out + if linked_bu_out and linked_bu_out.state == 'done': + raise UserError("Tidak bisa retur BU/PICK karena BU/OUT suda Done!") + else: + raise UserError("Dokumen Mungkin sudah done") - picking = self.operations - - if self.state != 'draft': - raise UserError("Submit hanya bisa dilakukan dari Draft.") - - if not self.operations: - raise UserError("Operations harus diisi!") - - if not self.return_type: - raise UserError("Return Type harus diisi!") - + if self._is_already_returned(self.operations): + raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") self._validate_product_lines() - if picking.picking_type_id.sequence_code == 'OUT' or picking.picking_type_id.id == 29: - if picking.state != 'done': - raise UserError("BU/OUT Harus Sudah DONE") - - elif picking.picking_type_id.sequence_code == 'PICK' or picking.picking_type_id.id == 30: - linked_bu_out = picking.linked_manual_bu_out - if linked_bu_out.state == 'done': - raise UserError("BU/PICK yang bisa diretur ketika BU/OUT yang belum DONE") - else: - raise UserError("Picking Type harus BU/OUT atau BU/PICK") + if self.state != 'draft': + raise UserError("Submit hanya bisa dilakukan dari Draft.") self.state = 'approval_sales' def action_approve(self): self.ensure_one() + self._validate_product_lines() if not self.operations: raise UserError("Operations harus diisi!") @@ -309,6 +325,8 @@ class TukarGuling(models.Model): if not record.operations: raise UserError("BU/OUT dari field operations tidak ditemukan.") + operation_picking = record.operations + related_pickings = self.env['stock.picking'].search([ ('origin', '=', record.origin), ('state', '=', 'done'), @@ -327,7 +345,6 @@ class TukarGuling(models.Model): created_returns = [] - # Lokasi default untuk retur srt_type = self.env['stock.picking.type'].browse(73) ort_type = self.env['stock.picking.type'].browse(74) @@ -354,25 +371,25 @@ class TukarGuling(models.Model): BU_STOCK_LOCATION_ID = 57 # Determine locations based on picking type - if picking.picking_type_id.id == 30: # -> ngeretur bu pick + if picking.picking_type_id.id == 30: # -> ngeretur bu pick return_type = ort_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID if not default_location_id or not default_location_dest_id: raise UserError("Lokasi Origin atau Destination salah.") - elif picking.picking_type_id.id == 29: # -> ngeretur bu out + elif picking.picking_type_id.id == 29: # -> ngeretur bu out return_type = srt_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID if not default_location_id or not default_location_dest_id: raise UserError("Lokasi Origin atau Destination salah.") - elif picking.picking_type_id.id == 74: # -> ngeretur srt + elif picking.picking_type_id.id == 74: # -> ngeretur srt return_type = bu_pick_type default_location_id = BU_STOCK_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID if not default_location_id or not default_location_dest_id: raise UserError("Lokasi Origin atau Destination salah.") - elif picking.picking_type_id.id == 73: # -> ngeretur ort + elif picking.picking_type_id.id == 73: # -> ngeretur ort return_type = bu_out_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID @@ -434,27 +451,24 @@ class TukarGuling(models.Model): return return_picking # Buat return dari BU/OUT - if record.operations.picking_type_id == 30: - for picking in [bu_pick_to_return]: - name = _create_return_from_picking(picking) - if name: - created_returns.append(name) + for picking in bu_out_to_return: + name = _create_return_from_picking(picking) + if name: + created_returns.append(name) # Buat return dari BU/PICK - else: - for picking in [bu_out_to_return]: - name = _create_return_from_picking(picking) - if name: - created_returns.append(name) - - # Buat return dari SRT dan ort - if record.return_type == 'tukar_guling' and record.operations.picking_type_id.id != 30: - target = [woi for woi in created_returns if woi.picking_type_id.id in (74, 73)] - for picking in target: - retur = _create_return_from_picking(picking) - if retur: - created_returns.append(retur) - + for picking in bu_pick_to_return: + name = _create_return_from_picking(picking) + if name: + created_returns.append(name) + + # Buat return dari SRT dan ort + if record.return_type == 'tukar_guling': + target = [woi for woi in created_returns if woi.picking_type_id.id in (74, 73)] + for picking in target: + retur = _create_return_from_picking(picking) + if retur: + created_returns.append(retur) if not created_returns: raise UserError("wkwkwk") @@ -498,4 +512,4 @@ class TukarGulingLine(models.Model): class StockPicking(models.Model): _inherit = 'stock.picking' - tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') \ No newline at end of file + tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') -- cgit v1.2.3 From 3c4f2eff3035f423d3bf0dc19d5aad457854e43a Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 27 Jun 2025 22:03:37 +0700 Subject: done aman --- indoteknik_custom/models/tukar_guling.py | 84 +++++++++++++++----------------- 1 file changed, 40 insertions(+), 44 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index df501261..42646fbf 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -234,8 +234,9 @@ class TukarGuling(models.Model): return new_record def write(self, vals): - if self._is_already_returned(self.operations): - raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + if self.operations.picking_type_id.id != 30: + if self._is_already_returned(self.operations): + raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: @@ -271,12 +272,10 @@ class TukarGuling(models.Model): elif picking.picking_type_id.id == 30: linked_bu_out = picking.linked_manual_bu_out if linked_bu_out and linked_bu_out.state == 'done': - raise UserError("Tidak bisa retur BU/PICK karena BU/OUT suda Done!") - else: - raise UserError("Dokumen Mungkin sudah done") - - if self._is_already_returned(self.operations): - raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT suda Done!") + if self.operations.picking_type_id.id != 30: + if self._is_already_returned(self.operations): + raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") self._validate_product_lines() @@ -371,34 +370,24 @@ class TukarGuling(models.Model): BU_STOCK_LOCATION_ID = 57 # Determine locations based on picking type - if picking.picking_type_id.id == 30: # -> ngeretur bu pick + if picking.picking_type_id.id == 30: return_type = ort_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID - if not default_location_id or not default_location_dest_id: - raise UserError("Lokasi Origin atau Destination salah.") - elif picking.picking_type_id.id == 29: # -> ngeretur bu out + elif picking.picking_type_id.id == 29: return_type = srt_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID - if not default_location_id or not default_location_dest_id: - raise UserError("Lokasi Origin atau Destination salah.") - elif picking.picking_type_id.id == 74: # -> ngeretur srt - return_type = bu_pick_type - default_location_id = BU_STOCK_LOCATION_ID - default_location_dest_id = BU_OUTPUT_LOCATION_ID - if not default_location_id or not default_location_dest_id: - raise UserError("Lokasi Origin atau Destination salah.") - elif picking.picking_type_id.id == 73: # -> ngeretur ort + elif picking.picking_type_id.id == 74: return_type = bu_out_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID - if not default_location_id or not default_location_dest_id: - raise UserError("Lokasi Origin atau Destination salah.") + elif picking.picking_type_id.id == 73: + return_type = bu_pick_type + default_location_id = BU_STOCK_LOCATION_ID + default_location_dest_id = BU_OUTPUT_LOCATION_ID else: - raise UserError("Hayo") return None - return_context = dict(self.env.context) return_context.update({ 'active_id': picking.id, @@ -450,25 +439,32 @@ class TukarGuling(models.Model): return return_picking - # Buat return dari BU/OUT - for picking in bu_out_to_return: - name = _create_return_from_picking(picking) - if name: - created_returns.append(name) - - # Buat return dari BU/PICK - for picking in bu_pick_to_return: - name = _create_return_from_picking(picking) - if name: - created_returns.append(name) - - # Buat return dari SRT dan ort - if record.return_type == 'tukar_guling': - target = [woi for woi in created_returns if woi.picking_type_id.id in (74, 73)] - for picking in target: - retur = _create_return_from_picking(picking) - if retur: - created_returns.append(retur) + if record.operations.picking_type_id.id == 30: + ort = _create_return_from_picking(record.operations) + if ort: + created_returns.append(ort) + else: + # CASE: Retur dari BU/OUT + srt = _create_return_from_picking(bu_out_to_return) + if srt: + created_returns.append(srt) + + ort = None + if bu_pick_to_return: + ort = _create_return_from_picking(bu_pick_to_return) + if ort: + created_returns.append(ort) + + if record.return_type == 'tukar_guling': + if srt: + bu_pick = _create_return_from_picking(srt) + if bu_pick: + created_returns.append(bu_pick) + + if ort: + bu_out = _create_return_from_picking(ort) + if bu_out: + created_returns.append(bu_out) if not created_returns: raise UserError("wkwkwk") -- cgit v1.2.3 From 90e26bd8d666503c2bb02608e3e5c26f15ab777b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 3 Jul 2025 11:44:57 +0700 Subject: rev validation bu pick and prepare for tukar guling po --- indoteknik_custom/models/tukar_guling.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 42646fbf..20eb598d 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -12,9 +12,9 @@ class TukarGuling(models.Model): _rec_name = 'name' origin = fields.Char(string='Origin SO') - + if_so = fields.Boolean('Is SO', default=True) + if_po = fields.Boolean('Is PO', default=False) real_shipping_id = fields.Many2one('res.partner', string='Shipping Address') - picking_ids = fields.One2many( 'stock.picking', 'tukar_guling_id', @@ -265,9 +265,9 @@ class TukarGuling(models.Model): def action_submit(self): self.ensure_one() - picking = self.env['stock.picking'] + picking = self.operations if picking.picking_type_id.id == 29: - if picking.picking_type_id.state != 'done': + if picking.state != 'done': raise UserError("BU/OUT belum Done!") elif picking.picking_type_id.id == 30: linked_bu_out = picking.linked_manual_bu_out -- cgit v1.2.3 From de50bcf16b21abf7b8e45fb59b366c594bc00038 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 3 Jul 2025 19:55:49 +0700 Subject: start tukar guling po --- indoteknik_custom/models/tukar_guling.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 20eb598d..eeec2d80 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -44,7 +44,7 @@ class TukarGuling(models.Model): notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ ('tukar_guling', 'Tukar Guling'), # -> barang yang sama - ('revisi_so', 'Revisi SO')]) + ('revisi_so', 'Revisi SO')], required=True) state = fields.Selection(string='Status', selection=[ ('draft', 'Draft'), ('approval_sales', ' Approval Sales'), @@ -457,15 +457,15 @@ class TukarGuling(models.Model): if record.return_type == 'tukar_guling': if srt: - bu_pick = _create_return_from_picking(srt) - if bu_pick: - created_returns.append(bu_pick) - - if ort: - bu_out = _create_return_from_picking(ort) + bu_out = _create_return_from_picking(srt) if bu_out: created_returns.append(bu_out) + if ort: + bu_pick = _create_return_from_picking(ort) + if bu_pick: + created_returns.append(bu_pick) + if not created_returns: raise UserError("wkwkwk") -- cgit v1.2.3 From c1bde5a538b04186a5c83aea2817cd4f05f2acd7 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 4 Jul 2025 09:26:26 +0700 Subject: fix orig id and dest id --- indoteknik_custom/models/tukar_guling.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index eeec2d80..db82ce1b 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -371,21 +371,29 @@ class TukarGuling(models.Model): # Determine locations based on picking type if picking.picking_type_id.id == 30: + # BU/PICK → ORT return_type = ort_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID + + elif picking.picking_type_id.id == 74: + # ORT → BU/PICK + return_type = bu_pick_type + default_location_id = BU_STOCK_LOCATION_ID + default_location_dest_id = BU_OUTPUT_LOCATION_ID + elif picking.picking_type_id.id == 29: + # BU/OUT → SRT return_type = srt_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID - elif picking.picking_type_id.id == 74: + + elif picking.picking_type_id.id == 73: + # SRT → BU/OUT return_type = bu_out_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID - elif picking.picking_type_id.id == 73: - return_type = bu_pick_type - default_location_id = BU_STOCK_LOCATION_ID - default_location_dest_id = BU_OUTPUT_LOCATION_ID + else: return None return_context = dict(self.env.context) @@ -456,16 +464,16 @@ class TukarGuling(models.Model): created_returns.append(ort) if record.return_type == 'tukar_guling': - if srt: - bu_out = _create_return_from_picking(srt) - if bu_out: - created_returns.append(bu_out) - if ort: bu_pick = _create_return_from_picking(ort) if bu_pick: created_returns.append(bu_pick) + if srt: + bu_out = _create_return_from_picking(srt) + if bu_out: + created_returns.append(bu_out) + if not created_returns: raise UserError("wkwkwk") -- cgit v1.2.3 From 1ed9ce9ab59c12fa378cfab02f8919e08f424853 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 8 Jul 2025 09:31:01 +0700 Subject: rev 77 --- indoteknik_custom/models/tukar_guling.py | 34 +++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index db82ce1b..339a1ff1 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -59,6 +59,10 @@ class TukarGuling(models.Model): @api.onchange('operations') def _onchange_operations(self): """Auto-populate lines ketika operations dipilih""" + for rec in self: + if rec.operations and rec.operations.picking_type_id.id == 30: + rec.return_type = 'revisi_so' + if self.operations: from_return_picking = self.env.context.get('from_return_picking', False) or \ self.env.context.get('default_line_ids', False) @@ -234,9 +238,11 @@ class TukarGuling(models.Model): return new_record def write(self, vals): - if self.operations.picking_type_id.id != 30: - if self._is_already_returned(self.operations): - raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + if self.operations.picking_type_id.id == 30 and self.return_type == 'tukar_guling': + raise UserError ("BU/PICK tidak boleh retur tukar guling") + # if self.operations.picking_type_id.id != 30: + # if self._is_already_returned(self.operations): + # raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: @@ -244,6 +250,12 @@ class TukarGuling(models.Model): return super(TukarGuling, self).write(vals) + def unlink(self): + for record in self: + if record.state == 'done': + raise UserError("Tidak bisa hapus pengajuan jika sudah done, set ke draft terlebih dahulu") + return super(TukarGuling, self).unlink() + def action_view_picking(self): self.ensure_one() action = self.env.ref('stock.action_picking_tree_all').read()[0] @@ -266,6 +278,8 @@ class TukarGuling(models.Model): def action_submit(self): self.ensure_one() picking = self.operations + if picking.picking_type_id.id == 30 and self.return_type == 'tukar_guling': + raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") if picking.picking_type_id.id == 29: if picking.state != 'done': raise UserError("BU/OUT belum Done!") @@ -287,6 +301,9 @@ class TukarGuling(models.Model): self.ensure_one() self._validate_product_lines() + if self.operations.picking_type_id.id == 30 and self.return_type == 'tukar_guling': + raise UserError ("BU/PICK tidak boleh retur tukar guling") + if not self.operations: raise UserError("Operations harus diisi!") @@ -315,6 +332,14 @@ class TukarGuling(models.Model): def action_cancel(self): self.ensure_one() + # picking = self.env['stock.picking'] + bu_done = self.picking_ids.filtered(lambda p: p.state == 'done') + if bu_done: + raise UserError("Dokuemn BU sudah Done, tidak bisa di cancel") + ongoing_bu = self.picking_ids.filtered(lambda p: p.state != 'done') + for picking in ongoing_bu: + picking.action_cancel() + # if self.state == 'done': # raise UserError("Tidak bisa cancel jika sudah done") self.state = 'cancel' @@ -339,6 +364,9 @@ class TukarGuling(models.Model): bu_pick_to_return = record.operations.konfirm_koli_lines.pick_id bu_out_to_return = record.operations + # if bu_pick_to_return and self.return_type == 'tukar_guling': + # raise UserError("BU/PICK tidak boleh di retur tukar guling") + if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") -- cgit v1.2.3 From 52ed14a408f63bc7d93b7bc393a8a6eb4cd1ac08 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 8 Jul 2025 09:39:48 +0700 Subject: rev 77 check invoice --- indoteknik_custom/models/tukar_guling.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 339a1ff1..1aa5af5c 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -193,6 +193,20 @@ class TukarGuling(models.Model): ('state', '!=', 'cancel') ]) > 0 + @api.onchange('return_type', 'operations') + def _onchange_check_invoice(self): + for record in self: + if record.return_type == 'revisi_so' and record.origin: + # Cek invoice yang berhubungan dengan origin SO + invoices = self.env['account.move'].search([ + ('invoice_origin', 'ilike', record.origin), + ('state', 'not in', ['draft', 'cancel']) + ]) + if invoices: + raise UserError( + _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen SO %s sudah dibuat invoice.") % record.origin + ) + @api.model def create(self, vals): # Generate sequence number @@ -290,6 +304,7 @@ class TukarGuling(models.Model): if self.operations.picking_type_id.id != 30: if self._is_already_returned(self.operations): raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + self._onchange_check_invoice() self._validate_product_lines() @@ -300,6 +315,7 @@ class TukarGuling(models.Model): def action_approve(self): self.ensure_one() self._validate_product_lines() + self._onchange_check_invoice() if self.operations.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError ("BU/PICK tidak boleh retur tukar guling") -- cgit v1.2.3 From a8886eb056d62ace630f15fb3eaa5d55a360b277 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 8 Jul 2025 10:16:46 +0700 Subject: rev 77 check invoice --- indoteknik_custom/models/tukar_guling.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 1aa5af5c..9fe7527c 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -193,18 +193,17 @@ class TukarGuling(models.Model): ('state', '!=', 'cancel') ]) > 0 - @api.onchange('return_type', 'operations') - def _onchange_check_invoice(self): + @api.constrains('return_type', 'operations') + def _check_invoice_on_revisi_so(self): for record in self: if record.return_type == 'revisi_so' and record.origin: - # Cek invoice yang berhubungan dengan origin SO invoices = self.env['account.move'].search([ ('invoice_origin', 'ilike', record.origin), ('state', 'not in', ['draft', 'cancel']) ]) if invoices: - raise UserError( - _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen SO %s sudah dibuat invoice.") % record.origin + raise ValidationError( + _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen %s sudah dibuat invoice.") % record.origin ) @api.model @@ -252,6 +251,7 @@ class TukarGuling(models.Model): return new_record def write(self, vals): + self._check_invoice_on_revisi_so() if self.operations.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError ("BU/PICK tidak boleh retur tukar guling") # if self.operations.picking_type_id.id != 30: @@ -304,7 +304,7 @@ class TukarGuling(models.Model): if self.operations.picking_type_id.id != 30: if self._is_already_returned(self.operations): raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") - self._onchange_check_invoice() + self._check_invoice_on_revisi_so() self._validate_product_lines() @@ -315,7 +315,7 @@ class TukarGuling(models.Model): def action_approve(self): self.ensure_one() self._validate_product_lines() - self._onchange_check_invoice() + self._check_invoice_on_revisi_so() if self.operations.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError ("BU/PICK tidak boleh retur tukar guling") -- cgit v1.2.3 From 21128e0f165045558c2c8ef6faf199d4379614b1 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 9 Jul 2025 09:54:13 +0700 Subject: rev 77 vals --- indoteknik_custom/models/tukar_guling.py | 50 +++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 9fe7527c..e2f68e6c 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -251,9 +251,25 @@ class TukarGuling(models.Model): return new_record def write(self, vals): + self.ensure_one() self._check_invoice_on_revisi_so() - if self.operations.picking_type_id.id == 30 and self.return_type == 'tukar_guling': - raise UserError ("BU/PICK tidak boleh retur tukar guling") + operasi = self.operations.picking_type_id.id + tipe = self.return_type + pp = vals.get('return_type', tipe) + + if not self.operations: + raise UserError("Operations harus diisi!") + + if not self.return_type: + raise UserError("Return Type harus diisi!") + + if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': + raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") + if operasi == 30 and pp == 'tukar_guling': + raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") + else: + _logger.info("hehhe") + # if self.operations.picking_type_id.id != 30: # if self._is_already_returned(self.operations): # raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") @@ -267,7 +283,11 @@ class TukarGuling(models.Model): def unlink(self): for record in self: if record.state == 'done': - raise UserError("Tidak bisa hapus pengajuan jika sudah done, set ke draft terlebih dahulu") + 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') + for picking in ongoing_bu: + picking.action_cancel() return super(TukarGuling, self).unlink() def action_view_picking(self): @@ -291,6 +311,15 @@ class TukarGuling(models.Model): def action_submit(self): self.ensure_one() + + existing_tukar_guling = self.env['tukar.guling'].search([ + ('operations', '=', self.operations.id), + ('id', '!=', self.id), + ('state', '!=', 'cancel'), + ], limit=1) + + if existing_tukar_guling: + raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations if picking.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") @@ -307,7 +336,6 @@ class TukarGuling(models.Model): self._check_invoice_on_revisi_so() self._validate_product_lines() - if self.state != 'draft': raise UserError("Submit hanya bisa dilakukan dari Draft.") self.state = 'approval_sales' @@ -317,8 +345,16 @@ class TukarGuling(models.Model): self._validate_product_lines() self._check_invoice_on_revisi_so() - if self.operations.picking_type_id.id == 30 and self.return_type == 'tukar_guling': - raise UserError ("BU/PICK tidak boleh retur tukar guling") + operasi = self.operations.picking_type_id.id + tipe = self.return_type + pp = vals.get('return_type', tipe) + + if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': + raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") + if operasi == 30 and pp == 'tukar_guling': + raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") + else: + _logger.info("hehhe") if not self.operations: raise UserError("Operations harus diisi!") @@ -351,7 +387,7 @@ class TukarGuling(models.Model): # picking = self.env['stock.picking'] bu_done = self.picking_ids.filtered(lambda p: p.state == 'done') if bu_done: - raise UserError("Dokuemn BU sudah Done, tidak bisa di cancel") + raise UserError("Dokuemen BU sudah Done, tidak bisa di cancel") ongoing_bu = self.picking_ids.filtered(lambda p: p.state != 'done') for picking in ongoing_bu: picking.action_cancel() -- cgit v1.2.3 From 717dbfa7070e94c0af2bb39e2cebb4dc71d123b9 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 9 Jul 2025 13:14:59 +0700 Subject: rev 77 vals vals --- indoteknik_custom/models/tukar_guling.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index e2f68e6c..5762abbb 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -270,9 +270,7 @@ class TukarGuling(models.Model): else: _logger.info("hehhe") - # if self.operations.picking_type_id.id != 30: - # if self._is_already_returned(self.operations): - # raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: @@ -330,9 +328,8 @@ class TukarGuling(models.Model): linked_bu_out = picking.linked_manual_bu_out if linked_bu_out and linked_bu_out.state == 'done': raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT suda Done!") - if self.operations.picking_type_id.id != 30: - if self._is_already_returned(self.operations): - raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + if self._is_already_returned(self.operations): + raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") self._check_invoice_on_revisi_so() self._validate_product_lines() @@ -527,10 +524,12 @@ class TukarGuling(models.Model): return return_picking + # CASE: Kalau retur BU/PICK jadi ort saja if record.operations.picking_type_id.id == 30: ort = _create_return_from_picking(record.operations) if ort: created_returns.append(ort) + # Kalau retur BU/OUT else: # CASE: Retur dari BU/OUT srt = _create_return_from_picking(bu_out_to_return) -- cgit v1.2.3 From f6229d1426fc0823e1b29721f7fbaaec285351ef Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 14 Jul 2025 16:34:59 +0700 Subject: fix bugs --- indoteknik_custom/models/tukar_guling.py | 110 +++++++++++-------------------- 1 file changed, 38 insertions(+), 72 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 5762abbb..24dbf3d8 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -267,8 +267,8 @@ class TukarGuling(models.Model): raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") if operasi == 30 and pp == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") - else: - _logger.info("hehhe") + # else: + # _logger.info("hehhe") if 'operations' in vals and not vals.get('origin'): @@ -279,6 +279,8 @@ class TukarGuling(models.Model): return super(TukarGuling, self).write(vals) def unlink(self): + # if self.state == 'done': + # raise UserError ("Tidak Boleh delete ketika sudahh done") for record in self: if record.state == 'done': raise UserError( @@ -344,11 +346,10 @@ class TukarGuling(models.Model): operasi = self.operations.picking_type_id.id tipe = self.return_type - pp = vals.get('return_type', tipe) if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") - if operasi == 30 and pp == 'tukar_guling': + if operasi == 30 and tipe == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") else: _logger.info("hehhe") @@ -398,81 +399,52 @@ class TukarGuling(models.Model): if not record.operations: raise UserError("BU/OUT dari field operations tidak ditemukan.") - operation_picking = record.operations - - related_pickings = self.env['stock.picking'].search([ - ('origin', '=', record.origin), - ('state', '=', 'done'), - ('picking_type_id', 'in', [29, 30]) - ]) - if not related_pickings: - raise UserError( - "Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin) - - # filter based on stock.picking picking type bu_pick_to_return = record.operations.konfirm_koli_lines.pick_id bu_out_to_return = record.operations - # if bu_pick_to_return and self.return_type == 'tukar_guling': - # raise UserError("BU/PICK tidak boleh di retur tukar guling") - if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") created_returns = [] - # Lokasi default untuk retur + # Picking types & locations srt_type = self.env['stock.picking.type'].browse(73) ort_type = self.env['stock.picking.type'].browse(74) bu_pick_type = self.env['stock.picking.type'].browse(30) bu_out_type = self.env['stock.picking.type'].browse(29) - stock_location = self.env['stock.location'] - - srt_src = stock_location.browse(5) - srt_dest = stock_location.browse(60) - - ort_src = stock_location.browse(60) - ort_dest = stock_location.browse(57) - - if not ort_src or not ort_dest or not srt_src or not srt_dest: - raise UserError("salahwoi") - - # Fungsi membuat retur dari picking tertentu def _create_return_from_picking(picking): - grup = self.operations.group_id + if not picking: + return None + grup = record.operations.group_id PARTNER_LOCATION_ID = 5 BU_OUTPUT_LOCATION_ID = 60 BU_STOCK_LOCATION_ID = 57 - # Determine locations based on picking type if picking.picking_type_id.id == 30: # BU/PICK → ORT return_type = ort_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID - elif picking.picking_type_id.id == 74: # ORT → BU/PICK return_type = bu_pick_type default_location_id = BU_STOCK_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID - elif picking.picking_type_id.id == 29: # BU/OUT → SRT return_type = srt_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID - elif picking.picking_type_id.id == 73: # SRT → BU/OUT return_type = bu_out_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID - else: return None + return_context = dict(self.env.context) return_context.update({ 'active_id': picking.id, @@ -487,26 +459,26 @@ class TukarGuling(models.Model): 'original_location_id': default_location_id }) - # Create return lines return_lines = [] + # 🔥 Hanya pakai qty dari tukar guling line for line in record.line_ids: - move = picking.move_lines.filtered(lambda wkwk: wkwk.product_id == line.product_id) + move = picking.move_lines.filtered(lambda m: m.product_id == line.product_id) if move: return_lines.append((0, 0, { 'product_id': line.product_id.id, 'quantity': line.product_uom_qty, - 'move_id': move.id, + 'move_id': move[0].id, })) - if not move: - raise UserError("eror woi") + else: + raise UserError( + _("Tidak ditemukan move line di picking %s untuk produk %s") + % (picking.name, line.product_id.display_name) + ) + if not return_lines: - return None + raise UserError(_("Tidak ada product line valid untuk retur picking %s") % picking.name) return_wizard.product_return_moves = return_lines - - _logger.info("Creating return for picking %s", picking.name) - _logger.info("Default location src: %s", default_location_id) - _logger.info("Default location dest: %s", default_location_dest_id) return_vals = return_wizard.create_returns() return_id = return_vals.get('res_id') return_picking = self.env['stock.picking'].browse(return_id) @@ -514,7 +486,6 @@ class TukarGuling(models.Model): if not return_picking: raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) - # Force the destination location return_picking.write({ 'location_dest_id': default_location_dest_id, 'location_id': default_location_id, @@ -524,37 +495,32 @@ class TukarGuling(models.Model): return return_picking - # CASE: Kalau retur BU/PICK jadi ort saja - if record.operations.picking_type_id.id == 30: - ort = _create_return_from_picking(record.operations) + # === PERBAIKI URUTAN === + srt = _create_return_from_picking(bu_out_to_return) + if srt: + created_returns.append(srt) + + picks = self.env['stock.picking'].search([ + ('origin', '=', record.origin), + ('state', '=', 'done'), + ('picking_type_id', '=', 30) + ]) + for picking in picks: + ort = _create_return_from_picking(picking) if ort: created_returns.append(ort) - # Kalau retur BU/OUT - else: - # CASE: Retur dari BU/OUT - srt = _create_return_from_picking(bu_out_to_return) - if srt: - created_returns.append(srt) - - ort = None - if bu_pick_to_return: - ort = _create_return_from_picking(bu_pick_to_return) - if ort: - created_returns.append(ort) - - if record.return_type == 'tukar_guling': - if ort: + if record.return_type == 'tukar_guling': bu_pick = _create_return_from_picking(ort) if bu_pick: created_returns.append(bu_pick) - if srt: - bu_out = _create_return_from_picking(srt) - if bu_out: - created_returns.append(bu_out) + if record.return_type == 'tukar_guling' and srt: + bu_out = _create_return_from_picking(srt) + if bu_out: + created_returns.append(bu_out) if not created_returns: - raise UserError("wkwkwk") + raise UserError("Tidak ada dokumen retur yang berhasil dibuat.") class TukarGulingLine(models.Model): -- cgit v1.2.3 From 02cb11bce98b50e66760bc1b755367c0f7ba63f7 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 14 Jul 2025 17:04:54 +0700 Subject: fix bugs --- indoteknik_custom/models/tukar_guling.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 24dbf3d8..8d14012a 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -500,11 +500,7 @@ class TukarGuling(models.Model): if srt: created_returns.append(srt) - picks = self.env['stock.picking'].search([ - ('origin', '=', record.origin), - ('state', '=', 'done'), - ('picking_type_id', '=', 30) - ]) + picks = record.operations.konfirm_koli_lines.pick_id for picking in picks: ort = _create_return_from_picking(picking) if ort: -- cgit v1.2.3 From f3902ac1523b8c5c149a8661ad106363a8c01baf Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 14 Jul 2025 17:46:22 +0700 Subject: vals bu pick --- indoteknik_custom/models/tukar_guling.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 8d14012a..89f5994d 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -205,7 +205,6 @@ class TukarGuling(models.Model): raise ValidationError( _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen %s sudah dibuat invoice.") % record.origin ) - @api.model def create(self, vals): # Generate sequence number @@ -309,8 +308,18 @@ class TukarGuling(models.Model): else: raise UserError("Hanya record yang di-cancel yang bisa dikembalikan ke draft") + def _check_not_allow_tukar_guling_on_bu_pick(self, return_type=None): + operasi = self.operations.picking_type_id.id + tipe = return_type or self.return_type + + if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': + raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") + if operasi == 30 and tipe == 'tukar_guling': + raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") + def action_submit(self): self.ensure_one() + self._check_not_allow_tukar_guling_on_bu_pick() existing_tukar_guling = self.env['tukar.guling'].search([ ('operations', '=', self.operations.id), @@ -343,6 +352,7 @@ class TukarGuling(models.Model): self.ensure_one() self._validate_product_lines() self._check_invoice_on_revisi_so() + self._check_not_allow_tukar_guling_on_bu_pick() operasi = self.operations.picking_type_id.id tipe = self.return_type @@ -351,8 +361,8 @@ class TukarGuling(models.Model): raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") if operasi == 30 and tipe == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") - else: - _logger.info("hehhe") + # else: + # _logger.info("hehhe") if not self.operations: raise UserError("Operations harus diisi!") -- cgit v1.2.3 From 4f8f59f274e8ea19fcaa5f1c6b0e6e30400c66f7 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 15 Jul 2025 09:12:40 +0700 Subject: try --- indoteknik_custom/models/tukar_guling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 89f5994d..7e857d02 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -205,6 +205,7 @@ class TukarGuling(models.Model): raise ValidationError( _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen %s sudah dibuat invoice.") % record.origin ) + @api.model def create(self, vals): # Generate sequence number @@ -269,7 +270,6 @@ class TukarGuling(models.Model): # else: # _logger.info("hehhe") - if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: -- cgit v1.2.3 From 77d3bd2541a52270f03b84e38dd691630bcd82af Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 08:47:04 +0700 Subject: mapping koli tukar guling --- indoteknik_custom/models/tukar_guling.py | 77 +++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 16 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 7e857d02..f0fe13f6 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -55,6 +55,7 @@ class TukarGuling(models.Model): ], default='draft', tracking=True, required=True) line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') + mapping_koli_ids = fields.One2many('tukar.guling.mapping.koli', 'tukar_guling_id', string='Mapping Koli') @api.onchange('operations') def _onchange_operations(self): @@ -71,10 +72,10 @@ class TukarGuling(models.Model): # Hanya update origin, jangan ubah lines if self.operations.origin: self.origin = self.operations.origin - return # Clear existing lines hanya jika tidak dari return picking self.line_ids = [(5, 0, 0)] + self.mapping_koli_ids = [(5, 0, 0)] # Clear existing mapping koli juga # Set origin dari operations if self.operations.origin: @@ -94,26 +95,59 @@ class TukarGuling(models.Model): elif hasattr(self.operations, 'move_lines') and self.operations.move_lines: moves_to_check = self.operations.move_lines + # Collect product data for both lines and mapping koli + product_data = {} for move in moves_to_check: - _logger.info( - f"Move: {move.name}, Product: {move.product_id.name if move.product_id else 'No Product'}, Qty: {move.product_uom_qty}, State: {move.state}") - - # Ambil semua move yang ada quantity if move.product_id and move.product_uom_qty > 0: - lines_data.append((0, 0, { - 'sequence': sequence, - 'product_id': move.product_id.id, - 'product_uom_qty': move.product_uom_qty, - 'product_uom': move.product_uom.id, - 'name': move.name or move.product_id.display_name, - })) - sequence += 10 + product_id = move.product_id.id + if product_id not in product_data: + product_data[product_id] = { + 'product': move.product_id, + 'qty': move.product_uom_qty, + 'uom': move.product_uom.id, + 'name': move.name or move.product_id.display_name + } + + # Create lines_data for product lines + for product_id, data in product_data.items(): + lines_data.append((0, 0, { + 'sequence': sequence, + 'product_id': product_id, + 'product_uom_qty': data['qty'], + 'product_uom': data['uom'], + 'name': data['name'], + })) + sequence += 10 if lines_data: self.line_ids = lines_data - _logger.info(f"Created {len(lines_data)} lines") + _logger.info(f"Created {len(lines_data)} product lines") + + # Prepare mapping koli based on picking type + mapping_koli_data = [] + sequence = 10 + + # Case 1: BU/OUT (picking_type_id.id == 29) + if self.operations.picking_type_id.id == 29: + # Ambil dari konfirm_koli_lines BU/OUT + for koli_line in self.operations.konfirm_koli_lines: + if koli_line.pick_id.move_line_ids_without_package.product_id.id in product_data: + mapping_koli_data.append((0, 0, { + 'sequence': sequence, + 'pick_id': koli_line.pick_id.move_line_ids_without_package.picking_id.id, + 'product_id': koli_line.pick_id.move_line_ids_without_package.product_id.id, + 'qty_done': koli_line.pick_id.move_line_ids_without_package.qty_done + })) + sequence += 10 + + + if mapping_koli_data: + self.mapping_koli_ids = mapping_koli_data + _logger.info(f"Created {len(mapping_koli_data)} mapping koli lines") + else: + _logger.info("No mapping koli lines created") else: - _logger.info("No lines created - no valid moves found") + _logger.info("No product lines created - no valid moves found") else: # Clear lines jika operations dikosongkan, kecuali dari return picking from_return_picking = self.env.context.get('from_return_picking', False) or \ @@ -121,6 +155,7 @@ class TukarGuling(models.Model): if not from_return_picking: self.line_ids = [(5, 0, 0)] + self.mapping_koli_ids = [(5, 0, 0)] self.origin = False @@ -470,7 +505,6 @@ class TukarGuling(models.Model): }) return_lines = [] - # 🔥 Hanya pakai qty dari tukar guling line for line in record.line_ids: move = picking.move_lines.filtered(lambda m: m.product_id == line.product_id) if move: @@ -568,3 +602,14 @@ class StockPicking(models.Model): _inherit = 'stock.picking' tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') + + +class TukarGulingMappingKoli(models.Model): + _name = 'tukar.guling.mapping.koli' + _description = 'Mapping Koli di Tukar Guling' + + tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling') + pick_id = fields.Many2one('stock.picking', string='BU PICK') + product_id = fields.Many2one('product.product', string='Product') + qty_done = fields.Float(string='Qty Done di BU PICK') + sequence = fields.Integer(string='Sequence', default=10) -- cgit v1.2.3 From 3b1bc7642b6a5d0ac19dd74563c5b353a7f3b8ba Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 10:27:02 +0700 Subject: vals qty mapping koli tukar guling --- indoteknik_custom/models/tukar_guling.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index f0fe13f6..333e2c8e 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -57,6 +57,26 @@ class TukarGuling(models.Model): line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') mapping_koli_ids = fields.One2many('tukar.guling.mapping.koli', 'tukar_guling_id', string='Mapping Koli') + @api.constrains('mapping_koli_ids') + def _check_mapping_koli(self): + for record in self: + if record.operations.picking_type_id.id == 29: # Only for BU/OUT + if not record.mapping_koli_ids: + raise UserError("❌ Mapping Koli belum diisi") + + # Calculate totals as integers + total_mapping_qty = sum(int(mapping.qty_done) for mapping in record.mapping_koli_ids) + total_line_qty = sum(int(line.product_uom_qty) for line in record.line_ids) + + # Strict integer comparison + if total_mapping_qty != total_line_qty: + raise UserError( + "❌ Total quantity mapping koli (%d) tidak sama dengan quantity retur (%d)" % + (total_mapping_qty, total_line_qty) + ) + else: + _logger.info("qty koli sesuai") + @api.onchange('operations') def _onchange_operations(self): """Auto-populate lines ketika operations dipilih""" -- cgit v1.2.3 From 0e330178d087b2b4cb519c4e078f0fe25a76dfe5 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 13:37:08 +0700 Subject: fix qty prod line and add qty return --- indoteknik_custom/models/tukar_guling.py | 51 ++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 16 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 333e2c8e..a2168f5b 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -65,13 +65,13 @@ class TukarGuling(models.Model): raise UserError("❌ Mapping Koli belum diisi") # Calculate totals as integers - total_mapping_qty = sum(int(mapping.qty_done) for mapping in record.mapping_koli_ids) + total_mapping_qty = sum(int(mapping.qty_done) for mapping in record.mapping_koli_ids.qty_return) total_line_qty = sum(int(line.product_uom_qty) for line in record.line_ids) # Strict integer comparison if total_mapping_qty != total_line_qty: raise UserError( - "❌ Total quantity mapping koli (%d) tidak sama dengan quantity retur (%d)" % + "❌ Total quantity return di mapping koli (%d) tidak sama dengan quantity retur product lines (%d)" % (total_mapping_qty, total_line_qty) ) else: @@ -92,6 +92,28 @@ class TukarGuling(models.Model): # Hanya update origin, jangan ubah lines if self.operations.origin: self.origin = self.operations.origin + _logger.info("📌 Menggunakan product lines dari return wizard, tidak populate ulang.") + + # 🚀 Tapi tetap populate mapping koli jika BU/OUT + if self.operations.picking_type_id.id == 29: + mapping_koli_data = [] + sequence = 10 + tg_product_ids = self.line_ids.mapped('product_id.id') + + for koli_line in self.operations.konfirm_koli_lines: + pick_move = koli_line.pick_id.move_line_ids_without_package + if pick_move.product_id.id in tg_product_ids: + mapping_koli_data.append((0, 0, { + 'sequence': sequence, + 'pick_id': koli_line.pick_id.id, + 'product_id': pick_move.product_id.id, + 'qty_done': pick_move.qty_done + })) + sequence += 10 + + self.mapping_koli_ids = mapping_koli_data + _logger.info(f"✅ Created {len(mapping_koli_data)} mapping koli lines (from return wizard)") + return # keluar supaya tidak populate ulang lines # Clear existing lines hanya jika tidak dari return picking self.line_ids = [(5, 0, 0)] @@ -108,14 +130,12 @@ class TukarGuling(models.Model): # Untuk Odoo 14, gunakan move_ids_without_package atau move_lines moves_to_check = [] - # 1. move_ids_without_package (standard di Odoo 14) if hasattr(self.operations, 'move_ids_without_package') and self.operations.move_ids_without_package: moves_to_check = self.operations.move_ids_without_package - # 2. move_lines (backup untuk versi lama) elif hasattr(self.operations, 'move_lines') and self.operations.move_lines: moves_to_check = self.operations.move_lines - # Collect product data for both lines and mapping koli + # Collect product data product_data = {} for move in moves_to_check: if move.product_id and move.product_uom_qty > 0: @@ -128,7 +148,7 @@ class TukarGuling(models.Model): 'name': move.name or move.product_id.display_name } - # Create lines_data for product lines + # Buat lines_data for product_id, data in product_data.items(): lines_data.append((0, 0, { 'sequence': sequence, @@ -143,24 +163,23 @@ class TukarGuling(models.Model): self.line_ids = lines_data _logger.info(f"Created {len(lines_data)} product lines") - # Prepare mapping koli based on picking type + # Prepare mapping koli jika BU/OUT mapping_koli_data = [] sequence = 10 - # Case 1: BU/OUT (picking_type_id.id == 29) if self.operations.picking_type_id.id == 29: - # Ambil dari konfirm_koli_lines BU/OUT + tg_product_ids = [p for p in product_data] for koli_line in self.operations.konfirm_koli_lines: - if koli_line.pick_id.move_line_ids_without_package.product_id.id in product_data: + pick_move = koli_line.pick_id.move_line_ids_without_package + if pick_move.product_id.id in tg_product_ids: mapping_koli_data.append((0, 0, { 'sequence': sequence, - 'pick_id': koli_line.pick_id.move_line_ids_without_package.picking_id.id, - 'product_id': koli_line.pick_id.move_line_ids_without_package.product_id.id, - 'qty_done': koli_line.pick_id.move_line_ids_without_package.qty_done + 'pick_id': koli_line.pick_id.id, + 'product_id': pick_move.product_id.id, + 'qty_done': pick_move.qty_done })) sequence += 10 - if mapping_koli_data: self.mapping_koli_ids = mapping_koli_data _logger.info(f"Created {len(mapping_koli_data)} mapping koli lines") @@ -169,7 +188,6 @@ class TukarGuling(models.Model): else: _logger.info("No product lines created - no valid moves found") else: - # Clear lines jika operations dikosongkan, kecuali dari return picking from_return_picking = self.env.context.get('from_return_picking', False) or \ self.env.context.get('default_line_ids', False) @@ -631,5 +649,6 @@ class TukarGulingMappingKoli(models.Model): tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling') pick_id = fields.Many2one('stock.picking', string='BU PICK') product_id = fields.Many2one('product.product', string='Product') - qty_done = fields.Float(string='Qty Done di BU PICK') + qty_done = fields.Float(string='Qty Done BU PICK') + qty_return = fields.Float(string='Qty yang mau diretur') sequence = fields.Integer(string='Sequence', default=10) -- cgit v1.2.3 From efa1650aae2bc2dca99624092adcc21f87dab648 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 17:40:22 +0700 Subject: retur blm sesuai --- indoteknik_custom/models/tukar_guling.py | 99 +++++++++++++++++--------------- 1 file changed, 54 insertions(+), 45 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index a2168f5b..2c39b547 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -57,25 +57,23 @@ class TukarGuling(models.Model): line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') mapping_koli_ids = fields.One2many('tukar.guling.mapping.koli', 'tukar_guling_id', string='Mapping Koli') - @api.constrains('mapping_koli_ids') def _check_mapping_koli(self): for record in self: if record.operations.picking_type_id.id == 29: # Only for BU/OUT if not record.mapping_koli_ids: raise UserError("❌ Mapping Koli belum diisi") - # Calculate totals as integers - total_mapping_qty = sum(int(mapping.qty_done) for mapping in record.mapping_koli_ids.qty_return) + # Calculate totals + total_mapping_qty = sum(int(mapping.qty_return) for mapping in record.mapping_koli_ids) total_line_qty = sum(int(line.product_uom_qty) for line in record.line_ids) - # Strict integer comparison if total_mapping_qty != total_line_qty: raise UserError( "❌ Total quantity return di mapping koli (%d) tidak sama dengan quantity retur product lines (%d)" % (total_mapping_qty, total_line_qty) ) else: - _logger.info("qty koli sesuai") + _logger.info("✅ Qty mapping koli sesuai dengan product lines") @api.onchange('operations') def _onchange_operations(self): @@ -107,7 +105,8 @@ class TukarGuling(models.Model): 'sequence': sequence, 'pick_id': koli_line.pick_id.id, 'product_id': pick_move.product_id.id, - 'qty_done': pick_move.qty_done + 'qty_done': pick_move.qty_done, + 'qty_return': 0 })) sequence += 10 @@ -482,47 +481,39 @@ class TukarGuling(models.Model): if not record.operations: raise UserError("BU/OUT dari field operations tidak ditemukan.") + created_returns = [] + bu_pick_to_return = record.operations.konfirm_koli_lines.pick_id bu_out_to_return = record.operations if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") - created_returns = [] - - # Picking types & locations srt_type = self.env['stock.picking.type'].browse(73) ort_type = self.env['stock.picking.type'].browse(74) bu_pick_type = self.env['stock.picking.type'].browse(30) bu_out_type = self.env['stock.picking.type'].browse(29) - def _create_return_from_picking(picking): + PARTNER_LOCATION_ID = 5 + BU_OUTPUT_LOCATION_ID = 60 + BU_STOCK_LOCATION_ID = 57 + + def _create_return_from_picking_grouped(picking): if not picking: return None - grup = record.operations.group_id - PARTNER_LOCATION_ID = 5 - BU_OUTPUT_LOCATION_ID = 60 - BU_STOCK_LOCATION_ID = 57 + grup = record.operations.group_id if picking.picking_type_id.id == 30: - # BU/PICK → ORT - return_type = ort_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID elif picking.picking_type_id.id == 74: - # ORT → BU/PICK - return_type = bu_pick_type default_location_id = BU_STOCK_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID elif picking.picking_type_id.id == 29: - # BU/OUT → SRT - return_type = srt_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID elif picking.picking_type_id.id == 73: - # SRT → BU/OUT - return_type = bu_out_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID else: @@ -542,58 +533,75 @@ class TukarGuling(models.Model): 'original_location_id': default_location_id }) + # 🔥 If BU/OUT, ambil qty dari mapping koli (group by product) return_lines = [] - for line in record.line_ids: - move = picking.move_lines.filtered(lambda m: m.product_id == line.product_id) - if move: + if picking.picking_type_id.id == 29 and record.mapping_koli_ids: + grouped_qty = {} + for map_line in record.mapping_koli_ids: + pid = map_line.product_id.id + grouped_qty[pid] = grouped_qty.get(pid, 0) + map_line.qty_return + + for pid, qty in grouped_qty.items(): + move = picking.move_lines.filtered(lambda mv: mv.product_id.id == pid) + if move: + return_lines.append((0, 0, { + 'product_id': pid, + 'quantity': qty, + 'move_id': move[0].id, + })) + else: + raise UserError(_("Tidak ditemukan move line di picking %s untuk produk %s") + % (picking.name, map_line.product_id.display_name)) + else: + # Default kalau bukan BU/OUT (misalnya ORT), retur full qty done + for move in picking.move_lines: return_lines.append((0, 0, { - 'product_id': line.product_id.id, - 'quantity': line.product_uom_qty, - 'move_id': move[0].id, + 'product_id': move.product_id.id, + 'quantity': move.quantity_done or move.product_uom_qty, + 'move_id': move.id, })) - else: - raise UserError( - _("Tidak ditemukan move line di picking %s untuk produk %s") - % (picking.name, line.product_id.display_name) - ) if not return_lines: raise UserError(_("Tidak ada product line valid untuk retur picking %s") % picking.name) return_wizard.product_return_moves = return_lines return_vals = return_wizard.create_returns() - return_id = return_vals.get('res_id') - return_picking = self.env['stock.picking'].browse(return_id) + return_picking = self.env['stock.picking'].browse(return_vals.get('res_id')) if not return_picking: raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) return_picking.write({ - 'location_dest_id': default_location_dest_id, 'location_id': default_location_id, + 'location_dest_id': default_location_dest_id, 'group_id': grup.id, 'tukar_guling_id': record.id, }) + for move in return_picking.move_lines: + move.write({ + 'location_id': default_location_id, + 'location_dest_id': default_location_dest_id, + }) + return return_picking - # === PERBAIKI URUTAN === - srt = _create_return_from_picking(bu_out_to_return) + # === FLOW === + srt = _create_return_from_picking_grouped(bu_out_to_return) if srt: created_returns.append(srt) - picks = record.operations.konfirm_koli_lines.pick_id - for picking in picks: - ort = _create_return_from_picking(picking) + for picking in bu_pick_to_return: + ort = _create_return_from_picking_grouped(picking) if ort: created_returns.append(ort) if record.return_type == 'tukar_guling': - bu_pick = _create_return_from_picking(ort) + bu_pick = _create_return_from_picking_grouped(ort) if bu_pick: created_returns.append(bu_pick) if record.return_type == 'tukar_guling' and srt: - bu_out = _create_return_from_picking(srt) + bu_out = _create_return_from_picking_grouped(srt) if bu_out: created_returns.append(bu_out) @@ -601,6 +609,7 @@ class TukarGuling(models.Model): raise UserError("Tidak ada dokumen retur yang berhasil dibuat.") + class TukarGulingLine(models.Model): _name = 'tukar.guling.line' _description = 'Tukar Guling Line' @@ -650,5 +659,5 @@ class TukarGulingMappingKoli(models.Model): pick_id = fields.Many2one('stock.picking', string='BU PICK') product_id = fields.Many2one('product.product', string='Product') qty_done = fields.Float(string='Qty Done BU PICK') - qty_return = fields.Float(string='Qty yang mau diretur') - sequence = fields.Integer(string='Sequence', default=10) + qty_return = fields.Float(string='Qty diretur') + sequence = fields.Integer(string='Sequence', default=10) \ No newline at end of file -- cgit v1.2.3 From 2c5f2513e9380da52d9893c8dc5ad148c298a882 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 21:49:53 +0700 Subject: temp --- indoteknik_custom/models/tukar_guling.py | 227 ++++++++++++++++--------------- 1 file changed, 119 insertions(+), 108 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 2c39b547..295ca5d9 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -481,13 +481,8 @@ class TukarGuling(models.Model): if not record.operations: raise UserError("BU/OUT dari field operations tidak ditemukan.") - created_returns = [] - - bu_pick_to_return = record.operations.konfirm_koli_lines.pick_id - bu_out_to_return = record.operations - - if not bu_pick_to_return and not bu_out_to_return: - raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") + bu_out = record.operations + mapping_koli = record.mapping_koli_ids srt_type = self.env['stock.picking.type'].browse(73) ort_type = self.env['stock.picking.type'].browse(74) @@ -498,116 +493,132 @@ class TukarGuling(models.Model): BU_OUTPUT_LOCATION_ID = 60 BU_STOCK_LOCATION_ID = 57 - def _create_return_from_picking_grouped(picking): - if not picking: - return None - - grup = record.operations.group_id - - if picking.picking_type_id.id == 30: - default_location_id = BU_OUTPUT_LOCATION_ID - default_location_dest_id = BU_STOCK_LOCATION_ID - elif picking.picking_type_id.id == 74: - default_location_id = BU_STOCK_LOCATION_ID - default_location_dest_id = BU_OUTPUT_LOCATION_ID - elif picking.picking_type_id.id == 29: - default_location_id = PARTNER_LOCATION_ID - default_location_dest_id = BU_OUTPUT_LOCATION_ID - elif picking.picking_type_id.id == 73: - default_location_id = BU_OUTPUT_LOCATION_ID - default_location_dest_id = PARTNER_LOCATION_ID - else: - return None + created_returns = [] - return_context = dict(self.env.context) - return_context.update({ - 'active_id': picking.id, - 'default_location_id': default_location_id, - 'default_location_dest_id': default_location_dest_id, - 'from_ui': False, - }) + ### ============= SRT dari BU/OUT ================== + srt_return_lines = [] + for prod in mapping_koli.mapped('product_id'): + qty_total_return = sum(mk.qty_return for mk in mapping_koli.filtered(lambda m: m.product_id == prod)) + move = bu_out.move_lines.filtered(lambda m: m.product_id == prod) + if not move: + raise UserError(f"Tidak ditemukan move BU/OUT untuk product {prod.display_name}") + srt_return_lines.append((0, 0, { + 'product_id': prod.id, + 'quantity': qty_total_return, + 'move_id': move[0].id, + })) - return_wizard = self.env['stock.return.picking'].with_context(return_context).create({ - 'picking_id': picking.id, - 'location_id': default_location_dest_id, - 'original_location_id': default_location_id + srt_picking = None + if srt_return_lines: + srt_context = { + 'active_id': bu_out.id, + 'default_location_id': PARTNER_LOCATION_ID, + 'default_location_dest_id': BU_OUTPUT_LOCATION_ID, + 'from_ui': False, + } + srt_wizard = self.env['stock.return.picking'].with_context(srt_context).create({ + 'picking_id': bu_out.id, + 'location_id': BU_OUTPUT_LOCATION_ID, + 'original_location_id': PARTNER_LOCATION_ID, + 'product_return_moves': srt_return_lines }) - - # 🔥 If BU/OUT, ambil qty dari mapping koli (group by product) - return_lines = [] - if picking.picking_type_id.id == 29 and record.mapping_koli_ids: - grouped_qty = {} - for map_line in record.mapping_koli_ids: - pid = map_line.product_id.id - grouped_qty[pid] = grouped_qty.get(pid, 0) + map_line.qty_return - - for pid, qty in grouped_qty.items(): - move = picking.move_lines.filtered(lambda mv: mv.product_id.id == pid) - if move: - return_lines.append((0, 0, { - 'product_id': pid, - 'quantity': qty, - 'move_id': move[0].id, - })) - else: - raise UserError(_("Tidak ditemukan move line di picking %s untuk produk %s") - % (picking.name, map_line.product_id.display_name)) - else: - # Default kalau bukan BU/OUT (misalnya ORT), retur full qty done - for move in picking.move_lines: - return_lines.append((0, 0, { - 'product_id': move.product_id.id, - 'quantity': move.quantity_done or move.product_uom_qty, - 'move_id': move.id, - })) - - if not return_lines: - raise UserError(_("Tidak ada product line valid untuk retur picking %s") % picking.name) - - return_wizard.product_return_moves = return_lines - return_vals = return_wizard.create_returns() - return_picking = self.env['stock.picking'].browse(return_vals.get('res_id')) - - if not return_picking: - raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) - - return_picking.write({ - 'location_id': default_location_id, - 'location_dest_id': default_location_dest_id, - 'group_id': grup.id, + srt_vals = srt_wizard.create_returns() + srt_picking = self.env['stock.picking'].browse(srt_vals.get('res_id')) + srt_picking.write({ + 'location_id': PARTNER_LOCATION_ID, + 'location_dest_id': BU_OUTPUT_LOCATION_ID, + 'group_id': bu_out.group_id.id, 'tukar_guling_id': record.id, }) - - for move in return_picking.move_lines: - move.write({ - 'location_id': default_location_id, - 'location_dest_id': default_location_dest_id, + created_returns.append(srt_picking) + + ### ============= ORT dari BU/PICK ================== + ort_pickings = [] + for pick in mapping_koli.mapped('pick_id'): + ort_return_lines = [] + pick_lines = mapping_koli.filtered(lambda m: m.pick_id == pick) + for mk in pick_lines: + move = pick.move_lines.filtered(lambda m: m.product_id == mk.product_id) + if not move: + raise UserError( + f"Tidak ditemukan move di BU/PICK {pick.name} untuk {mk.product_id.display_name}") + ort_return_lines.append((0, 0, { + 'product_id': mk.product_id.id, + 'quantity': mk.qty_return, + 'move_id': move[0].id + })) + if ort_return_lines: + ort_context = { + 'active_id': pick.id, + 'default_location_id': BU_STOCK_LOCATION_ID, + 'default_location_dest_id': BU_OUTPUT_LOCATION_ID, + 'from_ui': False, + } + ort_wizard = self.env['stock.return.picking'].with_context(ort_context).create({ + 'picking_id': pick.id, + 'location_id': BU_OUTPUT_LOCATION_ID, + 'original_location_id': BU_STOCK_LOCATION_ID, + 'product_return_moves': ort_return_lines }) - - return return_picking - - # === FLOW === - srt = _create_return_from_picking_grouped(bu_out_to_return) - if srt: - created_returns.append(srt) - - for picking in bu_pick_to_return: - ort = _create_return_from_picking_grouped(picking) - if ort: - created_returns.append(ort) - if record.return_type == 'tukar_guling': - bu_pick = _create_return_from_picking_grouped(ort) - if bu_pick: - created_returns.append(bu_pick) - - if record.return_type == 'tukar_guling' and srt: - bu_out = _create_return_from_picking_grouped(srt) - if bu_out: - created_returns.append(bu_out) + ort_vals = ort_wizard.create_returns() + ort_picking = self.env['stock.picking'].browse(ort_vals.get('res_id')) + ort_picking.write({ + 'location_id': BU_STOCK_LOCATION_ID, + 'location_dest_id': BU_OUTPUT_LOCATION_ID, + 'group_id': bu_out.group_id.id, + 'tukar_guling_id': record.id, + }) + ort_pickings.append(ort_picking) + created_returns.append(ort_picking) + + ### ============= BU/PICK & BU/OUT baru (tukar guling) ============== + if record.return_type == 'tukar_guling': + # Dari SRT → BU/OUT baru + if srt_picking: + bu_out_new = self.env['stock.return.picking'].with_context({ + 'active_id': srt_picking.id, + 'default_location_id': BU_OUTPUT_LOCATION_ID, + 'default_location_dest_id': PARTNER_LOCATION_ID, + 'from_ui': False, + }).create({ + 'picking_id': srt_picking.id, + 'location_id': PARTNER_LOCATION_ID, + 'original_location_id': BU_OUTPUT_LOCATION_ID + }).create_returns() + new_out = self.env['stock.picking'].browse(bu_out_new.get('res_id')) + new_out.write({ + 'location_id': BU_OUTPUT_LOCATION_ID, + 'location_dest_id': PARTNER_LOCATION_ID, + 'group_id': bu_out.group_id.id, + 'tukar_guling_id': record.id, + }) + created_returns.append(new_out) + + # Dari ORT → BU/PICK baru + for ort_p in ort_pickings: + bu_pick_new = self.env['stock.return.picking'].with_context({ + 'active_id': ort_p.id, + 'default_location_id': BU_OUTPUT_LOCATION_ID, + 'default_location_dest_id': BU_STOCK_LOCATION_ID, + 'from_ui': False, + }).create({ + 'picking_id': ort_p.id, + 'location_id': BU_STOCK_LOCATION_ID, + 'original_location_id': BU_OUTPUT_LOCATION_ID + }).create_returns() + new_pick = self.env['stock.picking'].browse(bu_pick_new.get('res_id')) + new_pick.write({ + 'location_id': BU_OUTPUT_LOCATION_ID, + 'location_dest_id': BU_STOCK_LOCATION_ID, + 'group_id': bu_out.group_id.id, + 'tukar_guling_id': record.id, + }) + created_returns.append(new_pick) if not created_returns: - raise UserError("Tidak ada dokumen retur yang berhasil dibuat.") + raise UserError("Tidak ada dokumen retur berhasil dibuat.") + _logger.info("✅ Created %s returns: %s", len(created_returns), ", ".join([p.name for p in created_returns])) class TukarGulingLine(models.Model): -- cgit v1.2.3 From 3c7d7783aa15fee124d94839822614de4049b9c1 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 17 Jul 2025 13:17:15 +0700 Subject: Done Tukar Guling SO --- indoteknik_custom/models/tukar_guling.py | 137 ++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 46 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 295ca5d9..ded4e2a3 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -477,6 +477,7 @@ class TukarGuling(models.Model): self.state = 'cancel' def _create_pickings(self): + _logger.info("🛠 Starting _create_pickings()") for record in self: if not record.operations: raise UserError("BU/OUT dari field operations tidak ditemukan.") @@ -484,141 +485,185 @@ class TukarGuling(models.Model): bu_out = record.operations mapping_koli = record.mapping_koli_ids + # Constants + PARTNER_LOCATION_ID = 5 + BU_OUTPUT_LOCATION_ID = 60 + BU_STOCK_LOCATION_ID = 57 + + # Picking Types srt_type = self.env['stock.picking.type'].browse(73) ort_type = self.env['stock.picking.type'].browse(74) bu_pick_type = self.env['stock.picking.type'].browse(30) bu_out_type = self.env['stock.picking.type'].browse(29) - PARTNER_LOCATION_ID = 5 - BU_OUTPUT_LOCATION_ID = 60 - BU_STOCK_LOCATION_ID = 57 - created_returns = [] - ### ============= SRT dari BU/OUT ================== + ### ======== SRT dari BU/OUT ========= srt_return_lines = [] for prod in mapping_koli.mapped('product_id'): - qty_total_return = sum(mk.qty_return for mk in mapping_koli.filtered(lambda m: m.product_id == prod)) + qty_total = sum(mk.qty_return for mk in mapping_koli.filtered(lambda m: m.product_id == prod)) move = bu_out.move_lines.filtered(lambda m: m.product_id == prod) if not move: - raise UserError(f"Tidak ditemukan move BU/OUT untuk product {prod.display_name}") + raise UserError(f"Move BU/OUT tidak ditemukan untuk produk {prod.display_name}") srt_return_lines.append((0, 0, { 'product_id': prod.id, - 'quantity': qty_total_return, - 'move_id': move[0].id, + 'quantity': qty_total, + 'move_id': move.id, })) + _logger.info(f"📟 SRT line: {prod.display_name} | qty={qty_total}") srt_picking = None if srt_return_lines: - srt_context = { + srt_wizard = self.env['stock.return.picking'].with_context({ 'active_id': bu_out.id, 'default_location_id': PARTNER_LOCATION_ID, 'default_location_dest_id': BU_OUTPUT_LOCATION_ID, 'from_ui': False, - } - srt_wizard = self.env['stock.return.picking'].with_context(srt_context).create({ + }).create({ 'picking_id': bu_out.id, - 'location_id': BU_OUTPUT_LOCATION_ID, - 'original_location_id': PARTNER_LOCATION_ID, + 'location_id': PARTNER_LOCATION_ID, + 'original_location_id': BU_OUTPUT_LOCATION_ID, 'product_return_moves': srt_return_lines }) srt_vals = srt_wizard.create_returns() - srt_picking = self.env['stock.picking'].browse(srt_vals.get('res_id')) + srt_picking = self.env['stock.picking'].browse(srt_vals['res_id']) srt_picking.write({ 'location_id': PARTNER_LOCATION_ID, 'location_dest_id': BU_OUTPUT_LOCATION_ID, 'group_id': bu_out.group_id.id, 'tukar_guling_id': record.id, + 'sale_order': record.origin }) created_returns.append(srt_picking) + _logger.info(f"✅ SRT created: {srt_picking.name}") - ### ============= ORT dari BU/PICK ================== + ### ======== ORT dari BU/PICK ========= ort_pickings = [] for pick in mapping_koli.mapped('pick_id'): ort_return_lines = [] - pick_lines = mapping_koli.filtered(lambda m: m.pick_id == pick) - for mk in pick_lines: + for mk in mapping_koli.filtered(lambda m: m.pick_id == pick): move = pick.move_lines.filtered(lambda m: m.product_id == mk.product_id) if not move: raise UserError( - f"Tidak ditemukan move di BU/PICK {pick.name} untuk {mk.product_id.display_name}") + f"Move tidak ditemukan di BU/PICK {pick.name} untuk {mk.product_id.display_name}") ort_return_lines.append((0, 0, { 'product_id': mk.product_id.id, 'quantity': mk.qty_return, - 'move_id': move[0].id + 'move_id': move.id, })) + _logger.info(f"📟 ORT line: {pick.name} | {mk.product_id.display_name} | qty={mk.qty_return}") + if ort_return_lines: - ort_context = { + ort_wizard = self.env['stock.return.picking'].with_context({ 'active_id': pick.id, - 'default_location_id': BU_STOCK_LOCATION_ID, - 'default_location_dest_id': BU_OUTPUT_LOCATION_ID, + 'default_location_id': BU_OUTPUT_LOCATION_ID, + 'default_location_dest_id': BU_STOCK_LOCATION_ID, 'from_ui': False, - } - ort_wizard = self.env['stock.return.picking'].with_context(ort_context).create({ + }).create({ 'picking_id': pick.id, 'location_id': BU_OUTPUT_LOCATION_ID, 'original_location_id': BU_STOCK_LOCATION_ID, 'product_return_moves': ort_return_lines }) ort_vals = ort_wizard.create_returns() - ort_picking = self.env['stock.picking'].browse(ort_vals.get('res_id')) + ort_picking = self.env['stock.picking'].browse(ort_vals['res_id']) ort_picking.write({ - 'location_id': BU_STOCK_LOCATION_ID, - 'location_dest_id': BU_OUTPUT_LOCATION_ID, + 'location_id': BU_OUTPUT_LOCATION_ID, + 'location_dest_id': BU_STOCK_LOCATION_ID, 'group_id': bu_out.group_id.id, 'tukar_guling_id': record.id, + 'sale_order': record.origin }) - ort_pickings.append(ort_picking) created_returns.append(ort_picking) + ort_pickings.append(ort_picking) + _logger.info(f"✅ ORT created: {ort_picking.name}") - ### ============= BU/PICK & BU/OUT baru (tukar guling) ============== + ### ======== Tukar Guling: BU/OUT dan BU/PICK baru ======== if record.return_type == 'tukar_guling': - # Dari SRT → BU/OUT baru + # BU/OUT Baru dari SRT if srt_picking: - bu_out_new = self.env['stock.return.picking'].with_context({ + return_lines = [] + for move in srt_picking.move_lines: + if move.product_uom_qty > 0: + return_lines.append((0, 0, { + 'product_id': move.product_id.id, + 'quantity': move.product_uom_qty, + 'move_id': move.id, + })) + _logger.info( + f"🔁 BU/OUT baru dari SRT | {move.product_id.display_name} | qty={move.product_uom_qty}") + + bu_out_wizard = self.env['stock.return.picking'].with_context({ 'active_id': srt_picking.id, 'default_location_id': BU_OUTPUT_LOCATION_ID, 'default_location_dest_id': PARTNER_LOCATION_ID, 'from_ui': False, }).create({ 'picking_id': srt_picking.id, - 'location_id': PARTNER_LOCATION_ID, - 'original_location_id': BU_OUTPUT_LOCATION_ID - }).create_returns() - new_out = self.env['stock.picking'].browse(bu_out_new.get('res_id')) + 'location_id': BU_OUTPUT_LOCATION_ID, + 'original_location_id': PARTNER_LOCATION_ID, + 'product_return_moves': return_lines + }) + bu_out_vals = bu_out_wizard.create_returns() + new_out = self.env['stock.picking'].browse(bu_out_vals['res_id']) new_out.write({ 'location_id': BU_OUTPUT_LOCATION_ID, 'location_dest_id': PARTNER_LOCATION_ID, 'group_id': bu_out.group_id.id, 'tukar_guling_id': record.id, + 'sale_order': record.origin }) created_returns.append(new_out) + _logger.info(f"✅ BU/OUT Baru dari SRT created: {new_out.name}") - # Dari ORT → BU/PICK baru + # BU/PICK Baru dari ORT for ort_p in ort_pickings: - bu_pick_new = self.env['stock.return.picking'].with_context({ + return_lines = [] + for move in ort_p.move_lines: + if move.product_uom_qty > 0: + return_lines.append((0, 0, { + 'product_id': move.product_id.id, + 'quantity': move.product_uom_qty, + 'move_id': move.id + })) + _logger.info( + f"🔁 BU/PICK baru dari ORT {ort_p.name} | {move.product_id.display_name} | qty={move.product_uom_qty}") + + if not return_lines: + _logger.warning(f"❌ Tidak ada qty > 0 di ORT {ort_p.name}, dilewati.") + continue + + bu_pick_wizard = self.env['stock.return.picking'].with_context({ 'active_id': ort_p.id, - 'default_location_id': BU_OUTPUT_LOCATION_ID, - 'default_location_dest_id': BU_STOCK_LOCATION_ID, + 'default_location_id': BU_STOCK_LOCATION_ID, + 'default_location_dest_id': BU_OUTPUT_LOCATION_ID, 'from_ui': False, }).create({ 'picking_id': ort_p.id, 'location_id': BU_STOCK_LOCATION_ID, - 'original_location_id': BU_OUTPUT_LOCATION_ID - }).create_returns() - new_pick = self.env['stock.picking'].browse(bu_pick_new.get('res_id')) + 'original_location_id': BU_OUTPUT_LOCATION_ID, + 'product_return_moves': return_lines + }) + bu_pick_vals = bu_pick_wizard.create_returns() + new_pick = self.env['stock.picking'].browse(bu_pick_vals['res_id']) new_pick.write({ - 'location_id': BU_OUTPUT_LOCATION_ID, - 'location_dest_id': BU_STOCK_LOCATION_ID, + 'location_id': BU_STOCK_LOCATION_ID, + 'location_dest_id': BU_OUTPUT_LOCATION_ID, 'group_id': bu_out.group_id.id, 'tukar_guling_id': record.id, + 'sale_order': record.origin }) + new_pick.action_assign() # Penting agar bisa trigger check koli + new_pick.action_confirm() created_returns.append(new_pick) + _logger.info(f"✅ BU/PICK Baru dari ORT created: {new_pick.name}") if not created_returns: raise UserError("Tidak ada dokumen retur berhasil dibuat.") - _logger.info("✅ Created %s returns: %s", len(created_returns), ", ".join([p.name for p in created_returns])) + _logger.info("✅ Finished _create_pickings(). Created %s returns: %s", + len(created_returns), + ", ".join([p.name for p in created_returns])) class TukarGulingLine(models.Model): -- cgit v1.2.3 From c18b38bc03d9c260532f4a8e956b51421283fa73 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 17 Jul 2025 13:42:53 +0700 Subject: FIx bug Tukar Guling SO --- indoteknik_custom/models/tukar_guling.py | 71 ++++++++++++++++---------------- 1 file changed, 36 insertions(+), 35 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index ded4e2a3..20d43e4c 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -580,41 +580,6 @@ class TukarGuling(models.Model): ### ======== Tukar Guling: BU/OUT dan BU/PICK baru ======== if record.return_type == 'tukar_guling': - # BU/OUT Baru dari SRT - if srt_picking: - return_lines = [] - for move in srt_picking.move_lines: - if move.product_uom_qty > 0: - return_lines.append((0, 0, { - 'product_id': move.product_id.id, - 'quantity': move.product_uom_qty, - 'move_id': move.id, - })) - _logger.info( - f"🔁 BU/OUT baru dari SRT | {move.product_id.display_name} | qty={move.product_uom_qty}") - - bu_out_wizard = self.env['stock.return.picking'].with_context({ - 'active_id': srt_picking.id, - 'default_location_id': BU_OUTPUT_LOCATION_ID, - 'default_location_dest_id': PARTNER_LOCATION_ID, - 'from_ui': False, - }).create({ - 'picking_id': srt_picking.id, - 'location_id': BU_OUTPUT_LOCATION_ID, - 'original_location_id': PARTNER_LOCATION_ID, - 'product_return_moves': return_lines - }) - bu_out_vals = bu_out_wizard.create_returns() - new_out = self.env['stock.picking'].browse(bu_out_vals['res_id']) - new_out.write({ - 'location_id': BU_OUTPUT_LOCATION_ID, - 'location_dest_id': PARTNER_LOCATION_ID, - 'group_id': bu_out.group_id.id, - 'tukar_guling_id': record.id, - 'sale_order': record.origin - }) - created_returns.append(new_out) - _logger.info(f"✅ BU/OUT Baru dari SRT created: {new_out.name}") # BU/PICK Baru dari ORT for ort_p in ort_pickings: @@ -658,6 +623,42 @@ class TukarGuling(models.Model): created_returns.append(new_pick) _logger.info(f"✅ BU/PICK Baru dari ORT created: {new_pick.name}") + # BU/OUT Baru dari SRT + if srt_picking: + return_lines = [] + for move in srt_picking.move_lines: + if move.product_uom_qty > 0: + return_lines.append((0, 0, { + 'product_id': move.product_id.id, + 'quantity': move.product_uom_qty, + 'move_id': move.id, + })) + _logger.info( + f"🔁 BU/OUT baru dari SRT | {move.product_id.display_name} | qty={move.product_uom_qty}") + + bu_out_wizard = self.env['stock.return.picking'].with_context({ + 'active_id': srt_picking.id, + 'default_location_id': BU_OUTPUT_LOCATION_ID, + 'default_location_dest_id': PARTNER_LOCATION_ID, + 'from_ui': False, + }).create({ + 'picking_id': srt_picking.id, + 'location_id': BU_OUTPUT_LOCATION_ID, + 'original_location_id': PARTNER_LOCATION_ID, + 'product_return_moves': return_lines + }) + bu_out_vals = bu_out_wizard.create_returns() + new_out = self.env['stock.picking'].browse(bu_out_vals['res_id']) + new_out.write({ + 'location_id': BU_OUTPUT_LOCATION_ID, + 'location_dest_id': PARTNER_LOCATION_ID, + 'group_id': bu_out.group_id.id, + 'tukar_guling_id': record.id, + 'sale_order': record.origin + }) + created_returns.append(new_out) + _logger.info(f"✅ BU/OUT Baru dari SRT created: {new_out.name}") + if not created_returns: raise UserError("Tidak ada dokumen retur berhasil dibuat.") -- cgit v1.2.3 From 2b55913a52b05f3f62786d7ae56070e96878178c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 17 Jul 2025 19:15:42 +0700 Subject: fix cannot retur bu pick tukar guling so --- indoteknik_custom/models/tukar_guling.py | 42 +++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 12 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 20d43e4c..d8e30006 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -539,19 +539,37 @@ class TukarGuling(models.Model): ### ======== ORT dari BU/PICK ========= ort_pickings = [] - for pick in mapping_koli.mapped('pick_id'): + 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 + + for pick in picks_to_return: ort_return_lines = [] - for mk in mapping_koli.filtered(lambda m: m.pick_id == pick): - move = pick.move_lines.filtered(lambda m: m.product_id == mk.product_id) - if not move: - raise UserError( - f"Move tidak ditemukan di BU/PICK {pick.name} untuk {mk.product_id.display_name}") - ort_return_lines.append((0, 0, { - 'product_id': mk.product_id.id, - 'quantity': mk.qty_return, - 'move_id': move.id, - })) - _logger.info(f"📟 ORT line: {pick.name} | {mk.product_id.display_name} | qty={mk.qty_return}") + if is_retur_from_bu_pick: + # Ambil dari tukar.guling.line + for line in record.line_ids: + move = pick.move_lines.filtered(lambda m: m.product_id == line.product_id) + if not move: + raise UserError( + f"Move tidak ditemukan di BU/PICK {pick.name} untuk {line.product_id.display_name}") + ort_return_lines.append((0, 0, { + 'product_id': line.product_id.id, + '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}") + else: + # Ambil dari mapping koli + for mk in mapping_koli.filtered(lambda m: m.pick_id == pick): + move = pick.move_lines.filtered(lambda m: m.product_id == mk.product_id) + if not move: + raise UserError( + f"Move tidak ditemukan di BU/PICK {pick.name} untuk {mk.product_id.display_name}") + ort_return_lines.append((0, 0, { + 'product_id': mk.product_id.id, + '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}") if ort_return_lines: ort_wizard = self.env['stock.return.picking'].with_context({ -- cgit v1.2.3 From be0e6d6e04c85f0c2f77a490074dbeb7de98be0f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 18 Jul 2025 17:29:14 +0700 Subject: validasi qty barang tidak sesuai --- indoteknik_custom/models/tukar_guling.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index d8e30006..a08d29bd 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -413,6 +413,14 @@ class TukarGuling(models.Model): raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT suda Done!") if self._is_already_returned(self.operations): raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + + for line in self.line_ids: + mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) + total_qty = sum(l.qty_return for l in mapping_lines) + if total_qty != line.product_uom_qty: + raise UserError( + _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) + self._check_invoice_on_revisi_so() self._validate_product_lines() @@ -429,6 +437,13 @@ class TukarGuling(models.Model): operasi = self.operations.picking_type_id.id tipe = self.return_type + for line in self.line_ids: + mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) + total_qty = sum(l.qty_return for l in mapping_lines) + if total_qty != line.product_uom_qty: + raise UserError( + _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) + if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") if operasi == 30 and tipe == 'tukar_guling': -- cgit v1.2.3 From 69f0fbf98183ea00bb069df3ecd40e91df7081c3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 22 Jul 2025 13:00:41 +0700 Subject: fix error when 2 item return --- indoteknik_custom/models/tukar_guling.py | 48 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index a08d29bd..e546ad9c 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -99,16 +99,16 @@ class TukarGuling(models.Model): tg_product_ids = self.line_ids.mapped('product_id.id') for koli_line in self.operations.konfirm_koli_lines: - pick_move = koli_line.pick_id.move_line_ids_without_package - if pick_move.product_id.id in tg_product_ids: - mapping_koli_data.append((0, 0, { - 'sequence': sequence, - 'pick_id': koli_line.pick_id.id, - 'product_id': pick_move.product_id.id, - 'qty_done': pick_move.qty_done, - 'qty_return': 0 - })) - sequence += 10 + for move in koli_line.pick_id.move_line_ids_without_package: + if move.product_id.id in tg_product_ids: + mapping_koli_data.append((0, 0, { + 'sequence': sequence, + 'pick_id': koli_line.pick_id.id, + 'product_id': move.product_id.id, + 'qty_done': move.qty_done, + 'qty_return': 0 + })) + sequence += 10 self.mapping_koli_ids = mapping_koli_data _logger.info(f"✅ Created {len(mapping_koli_data)} mapping koli lines (from return wizard)") @@ -128,7 +128,6 @@ class TukarGuling(models.Model): # Untuk Odoo 14, gunakan move_ids_without_package atau move_lines moves_to_check = [] - if hasattr(self.operations, 'move_ids_without_package') and self.operations.move_ids_without_package: moves_to_check = self.operations.move_ids_without_package elif hasattr(self.operations, 'move_lines') and self.operations.move_lines: @@ -160,7 +159,7 @@ class TukarGuling(models.Model): if lines_data: self.line_ids = lines_data - _logger.info(f"Created {len(lines_data)} product lines") + _logger.info(f"✅ Created {len(lines_data)} product lines") # Prepare mapping koli jika BU/OUT mapping_koli_data = [] @@ -169,23 +168,23 @@ class TukarGuling(models.Model): if self.operations.picking_type_id.id == 29: tg_product_ids = [p for p in product_data] for koli_line in self.operations.konfirm_koli_lines: - pick_move = koli_line.pick_id.move_line_ids_without_package - if pick_move.product_id.id in tg_product_ids: - mapping_koli_data.append((0, 0, { - 'sequence': sequence, - 'pick_id': koli_line.pick_id.id, - 'product_id': pick_move.product_id.id, - 'qty_done': pick_move.qty_done - })) - sequence += 10 + for move in koli_line.pick_id.move_line_ids_without_package: + if move.product_id.id in tg_product_ids: + mapping_koli_data.append((0, 0, { + 'sequence': sequence, + 'pick_id': koli_line.pick_id.id, + 'product_id': move.product_id.id, + 'qty_done': move.qty_done + })) + sequence += 10 if mapping_koli_data: self.mapping_koli_ids = mapping_koli_data - _logger.info(f"Created {len(mapping_koli_data)} mapping koli lines") + _logger.info(f"✅ Created {len(mapping_koli_data)} mapping koli lines") else: - _logger.info("No mapping koli lines created") + _logger.info("⚠️ No mapping koli lines created") else: - _logger.info("No product lines created - no valid moves found") + _logger.info("⚠️ No product lines created - no valid moves found") else: from_return_picking = self.env.context.get('from_return_picking', False) or \ self.env.context.get('default_line_ids', False) @@ -196,6 +195,7 @@ class TukarGuling(models.Model): self.origin = False + def action_populate_lines(self): """Manual button untuk populate lines - sebagai alternatif""" self.ensure_one() -- cgit v1.2.3 From 1de8ad79dd0f0832dd14de9c1d004884f153bec4 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 22 Jul 2025 16:42:10 +0700 Subject: Chatter and sequence --- indoteknik_custom/models/tukar_guling.py | 40 +++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index e546ad9c..f27446d0 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -1,15 +1,25 @@ from odoo import models, fields, api, _ from odoo.exceptions import UserError, ValidationError import logging +from datetime import datetime _logger = logging.getLogger(__name__) +#TODO +# 1. tracking status dokumen BU [X] +# 2. ganti nama dokumen +# 3. Tracking ketika create dokumen [X] +# 4. Tracking ketika ganti field operations, date approval (sales, finance, logistic) [X] +# 5. Ganti proses approval ke Sales, Finance, Logistic [X] +# 6. Make sure bu pick dan out tidak bisa diedit ketika ort dan srt blm done +# 7. change approval class TukarGuling(models.Model): _name = 'tukar.guling' _description = 'Tukar Guling' _order = 'date desc, id desc' _rec_name = 'name' + _inherit = ['mail.thread', 'mail.activity.mixin'] origin = fields.Char(string='Origin SO') if_so = fields.Boolean('Is SO', default=True) @@ -38,24 +48,28 @@ class TukarGuling(models.Model): ('state', '=', 'done'), ('linked_manual_bu_out', '!=', 'done'), ], - help='Nomor BU/OUT atau BU/PICK' + help='Nomor BU/OUT atau BU/PICK', tracking=3, + required=True ) ba_num = fields.Text('Nomor BA') notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ ('tukar_guling', 'Tukar Guling'), # -> barang yang sama - ('revisi_so', 'Revisi SO')], required=True) + ('revisi_so', 'Revisi SO')], required=True, tracking=3) state = fields.Selection(string='Status', selection=[ ('draft', 'Draft'), ('approval_sales', ' Approval Sales'), - ('approval_logistic', 'Approval Logistic'), ('approval_finance', 'Approval Finance'), + ('approval_logistic', 'Approval Logistic'), ('done', 'Done'), ('cancel', 'Canceled') ], default='draft', tracking=True, required=True) line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') mapping_koli_ids = fields.One2many('tukar.guling.mapping.koli', 'tukar_guling_id', string='Mapping Koli') + date_done = fields.Datetime('Approved Date Finance', tracking=3, readonly=True) + date_sales = fields.Datetime('Approved Date Sales', tracking=3, readonly=True) + date_logistic = fields.Datetime('Approved Date Logistic', tracking=3, readonly=True) def _check_mapping_koli(self): for record in self: @@ -295,7 +309,9 @@ class TukarGuling(models.Model): if picking.origin: vals['origin'] = picking.origin - return super(TukarGuling, self).create(vals) + res = super(TukarGuling, self).create(vals) + res.message_post(body=_("CCM Created By %s" ) % self.env.user.name) + return res def copy(self, default=None): if default is None: @@ -457,23 +473,29 @@ class TukarGuling(models.Model): if not self.return_type: raise UserError("Return Type harus diisi!") + now = datetime.now() + # Cek hak akses berdasarkan state for rec in self: if rec.state == 'approval_sales': if not rec.env.user.has_group('indoteknik_custom.group_role_sales'): raise UserError("Hanya Sales Manager yang boleh approve tahap ini.") rec.state = 'approval_logistic' - - 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 = 'approval_finance' + rec.date_sales = now elif rec.state == 'approval_finance': if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") rec.state = 'done' + rec.date_done = now rec._create_pickings() + + 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 = 'approval_finance' + rec.date_logistic = now + else: raise UserError("Status ini tidak bisa di-approve.") -- cgit v1.2.3 From 158c89a5048669f46d561aef3505042cc8c68a37 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 22 Jul 2025 19:51:47 +0700 Subject: change approval flow --- indoteknik_custom/models/tukar_guling.py | 48 +++++++++++++++++++------------- 1 file changed, 29 insertions(+), 19 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index f27446d0..2aaadecf 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -20,7 +20,8 @@ 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) if_po = fields.Boolean('Is PO', default=False) @@ -67,10 +68,15 @@ class TukarGuling(models.Model): line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') mapping_koli_ids = fields.One2many('tukar.guling.mapping.koli', 'tukar_guling_id', string='Mapping Koli') - date_done = fields.Datetime('Approved Date Finance', tracking=3, readonly=True) + date_finance = fields.Datetime('Approved Date Finance', tracking=3, readonly=True) date_sales = fields.Datetime('Approved Date Sales', tracking=3, readonly=True) date_logistic = fields.Datetime('Approved Date Logistic', tracking=3, readonly=True) + # @api.onchange('operations') + # def get_partner_id(self): + # if self.operations and self.operations.partner_id and self.operations.partner_id.name: + # self.partner_id == self.operations.partner_id.name + def _check_mapping_koli(self): for record in self: if record.operations.picking_type_id.id == 29: # Only for BU/OUT @@ -308,6 +314,8 @@ class TukarGuling(models.Model): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: vals['origin'] = picking.origin + if picking.partner_id: + vals['partner_id'] = picking.partner_id.id res = super(TukarGuling, self).create(vals) res.message_post(body=_("CCM Created By %s" ) % self.env.user.name) @@ -430,12 +438,13 @@ class TukarGuling(models.Model): if self._is_already_returned(self.operations): raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") - for line in self.line_ids: - mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) - total_qty = sum(l.qty_return for l in mapping_lines) - if total_qty != line.product_uom_qty: - raise UserError( - _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) + if self.operations.picking_type_id.id == 29: + for line in self.line_ids: + mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) + total_qty = sum(l.qty_return for l in mapping_lines) + if total_qty != line.product_uom_qty: + raise UserError( + _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) self._check_invoice_on_revisi_so() self._validate_product_lines() @@ -453,12 +462,13 @@ class TukarGuling(models.Model): operasi = self.operations.picking_type_id.id tipe = self.return_type - for line in self.line_ids: - mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) - total_qty = sum(l.qty_return for l in mapping_lines) - if total_qty != line.product_uom_qty: - raise UserError( - _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) + if self.operations.picking_type_id.id == 29: + for line in self.line_ids: + mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) + total_qty = sum(l.qty_return for l in mapping_lines) + if total_qty != line.product_uom_qty: + raise UserError( + _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") @@ -480,21 +490,21 @@ class TukarGuling(models.Model): if rec.state == 'approval_sales': if not rec.env.user.has_group('indoteknik_custom.group_role_sales'): raise UserError("Hanya Sales Manager yang boleh approve tahap ini.") - rec.state = 'approval_logistic' + rec.state = 'approval_finance' rec.date_sales = now elif rec.state == 'approval_finance': if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") - rec.state = 'done' - rec.date_done = now - rec._create_pickings() + rec.state = 'approval_logistic' + rec.date_finance = now 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 = 'approval_finance' + rec.state = 'done' rec.date_logistic = now + rec._create_pickings() else: raise UserError("Status ini tidak bisa di-approve.") -- cgit v1.2.3 From bf1171579f04b102b289e26c426034b2b4458839 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 22 Jul 2025 20:35:07 +0700 Subject: sequence so --- indoteknik_custom/models/tukar_guling.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 2aaadecf..63c8c27e 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -302,12 +302,7 @@ class TukarGuling(models.Model): def create(self, vals): # Generate sequence number if not vals.get('name') or vals['name'] == 'New': - sequence = self.env['ir.sequence'].search([('code', '=', 'tukar.guling')], limit=1) - if sequence: - vals['name'] = sequence.next_by_id() - else: - # Fallback jika sequence belum dibuat - vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') or 'PTG-00001' + vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') # Auto-fill origin from operations if not vals.get('origin') and vals.get('operations'): @@ -318,7 +313,7 @@ class TukarGuling(models.Model): vals['partner_id'] = picking.partner_id.id res = super(TukarGuling, self).create(vals) - res.message_post(body=_("CCM Created By %s" ) % self.env.user.name) + res.message_post(body=_("CCM Created By %s") % self.env.user.name) return res def copy(self, default=None): -- cgit v1.2.3 From a638ef831668f0bedbad8d6a5331f6422ca582dc Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 22 Jul 2025 22:18:53 +0700 Subject: track validate picking from tukar guling --- indoteknik_custom/models/tukar_guling.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 63c8c27e..e5f98467 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -767,6 +767,25 @@ class StockPicking(models.Model): tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') + def button_validate(self): + res = super(StockPicking, self).button_validate() + + for picking in self: + if picking.tukar_guling_id: + message = _( + "📦 %s Validated by %s Status Changed %s at %s." + ) % ( + 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' -- cgit v1.2.3 From 3614078d30a5dd148d11720963eb49cb2d2cc886 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 24 Jul 2025 09:19:43 +0700 Subject: tracking when create a new return --- indoteknik_custom/models/tukar_guling.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index e5f98467..cd9a1bd1 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -578,6 +578,8 @@ class TukarGuling(models.Model): }) created_returns.append(srt_picking) _logger.info(f"✅ SRT created: {srt_picking.name}") + record.message_post( + body=f"📦 {srt_picking.name} created by {self.env.user.name} (state: {srt_picking.state})") ### ======== ORT dari BU/PICK ========= ort_pickings = [] @@ -637,6 +639,8 @@ class TukarGuling(models.Model): created_returns.append(ort_picking) ort_pickings.append(ort_picking) _logger.info(f"✅ ORT created: {ort_picking.name}") + record.message_post( + body=f"📦 {ort_picking.name} created by {self.env.user.name} (state: {ort_picking.state})") ### ======== Tukar Guling: BU/OUT dan BU/PICK baru ======== if record.return_type == 'tukar_guling': @@ -682,6 +686,8 @@ class TukarGuling(models.Model): new_pick.action_confirm() created_returns.append(new_pick) _logger.info(f"✅ BU/PICK Baru dari ORT created: {new_pick.name}") + record.message_post( + body=f"📦 {new_pick.name} created by {self.env.user.name} (state: {new_pick.state})") # BU/OUT Baru dari SRT if srt_picking: @@ -718,6 +724,8 @@ class TukarGuling(models.Model): }) created_returns.append(new_out) _logger.info(f"✅ BU/OUT Baru dari SRT created: {new_out.name}") + record.message_post( + body=f"📦 {new_out.name} created by {self.env.user.name} (state: {new_out.state})") if not created_returns: raise UserError("Tidak ada dokumen retur berhasil dibuat.") -- cgit v1.2.3 From 4b1e8b9c5d516daf80e78212acfe28f7e518a4ba Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 24 Jul 2025 09:20:55 +0700 Subject: date logistic --- indoteknik_custom/models/tukar_guling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index cd9a1bd1..d40c0ac8 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -498,8 +498,8 @@ class TukarGuling(models.Model): 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.date_logistic = now rec._create_pickings() + rec.date_logistic = now else: raise UserError("Status ini tidak bisa di-approve.") -- cgit v1.2.3 From 6e3b560d3f115bfa89f3d5853cc9709b7cc963d6 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 24 Jul 2025 09:45:08 +0700 Subject: add permission to cancel ccm --- indoteknik_custom/models/tukar_guling.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index d40c0ac8..c5ccad80 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -507,6 +507,15 @@ class TukarGuling(models.Model): def action_cancel(self): self.ensure_one() # picking = self.env['stock.picking'] + + user = self.env.user + if not ( + user.has_group('indoteknik_custom.group_role_sales') or + user.has_group('indoteknik_custom.group_role_fat') or + user.has_group('indoteknik_custom.group_role_logistic') + ): + raise UserWarning('Anda tidak memiliki Permission untuk cancel document') + bu_done = self.picking_ids.filtered(lambda p: p.state == 'done') if bu_done: raise UserError("Dokuemen BU sudah Done, tidak bisa di cancel") -- cgit v1.2.3 From b8efc85091fe0af596872bffeb3cf6c78fe2beed Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 24 Jul 2025 17:35:11 +0700 Subject: cant delete when done and in approval state --- indoteknik_custom/models/tukar_guling.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/tukar_guling.py') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index c5ccad80..7253afb7 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -98,6 +98,8 @@ 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]: + 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: rec.return_type = 'revisi_so' @@ -343,6 +345,8 @@ class TukarGuling(models.Model): def write(self, vals): self.ensure_one() + 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 tipe = self.return_type @@ -756,6 +760,18 @@ class TukarGulingLine(models.Model): product_uom = fields.Many2one('uom.uom', string='Unit of Measure') name = fields.Text('Description') + @api.constrains('product_uom_qty') + def _check_qty_change_allowed(self): + for rec in self: + if rec.tukar_guling_id and rec.tukar_guling_id.state not in ['draft', 'cancel']: + raise ValidationError("Tidak bisa mengubah Quantity karena status dokumen bukan Draft atau Cancel.") + + def unlink(self): + for rec in self: + if rec.tukar_guling_id and rec.tukar_guling_id.state not in ['draft', 'cancel']: + raise UserError("Tidak bisa menghapus data karena status dokumen bukan Draft atau Cancel.") + return super(TukarGulingLine, self).unlink() + @api.model_create_multi def create(self, vals_list): """Override create to auto-assign sequence""" @@ -813,4 +829,15 @@ class TukarGulingMappingKoli(models.Model): product_id = fields.Many2one('product.product', string='Product') qty_done = fields.Float(string='Qty Done BU PICK') qty_return = fields.Float(string='Qty diretur') - sequence = fields.Integer(string='Sequence', default=10) \ No newline at end of file + sequence = fields.Integer(string='Sequence', default=10) + @api.constrains('qty_return') + def _check_qty_return_editable(self): + for rec in self: + if rec.tukar_guling_id and rec.tukar_guling_id.state not in ['draft', 'cancel']: + raise ValidationError("Tidak Bisa ubah qty retur jika status sudah approval atau done.") + + def unlink(self): + 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 -- cgit v1.2.3