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/__manifest__.py | 2 +- indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/stock_picking_return.py | 130 +++--- indoteknik_custom/models/tukar_guling.py | 14 +- indoteknik_custom/models/tukar_guling_po.py | 485 +++++++++++++++++++++++ indoteknik_custom/security/ir.model.access.csv | 2 + indoteknik_custom/views/ir_sequence.xml | 8 + indoteknik_custom/views/tukar_guling.xml | 49 ++- indoteknik_custom/views/tukar_guling_po.xml | 87 ++-- 9 files changed, 622 insertions(+), 156 deletions(-) create mode 100644 indoteknik_custom/models/tukar_guling_po.py diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 16b0e332..cf5556d1 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -170,7 +170,7 @@ 'views/sale_order_delay.xml', 'views/tukar_guling.xml', # 'views/tukar_guling_return_views.xml' - # 'views/tukar_guling_po.xml', + 'views/tukar_guling_po.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 8f08828b..903c0745 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -152,3 +152,4 @@ from . import stock_inventory from . import sale_order_delay from . import approval_invoice_date from . import tukar_guling +from . import tukar_guling_po diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py index 3442496d..a9781d3c 100644 --- a/indoteknik_custom/models/stock_picking_return.py +++ b/indoteknik_custom/models/stock_picking_return.py @@ -1,6 +1,8 @@ from odoo.exceptions import UserError from odoo.tools.float_utils import float_round from odoo import models, fields, api, _ +import logging +_logger = logging.getLogger(__name__) class StockReturnPicking(models.TransientModel): @@ -27,116 +29,86 @@ class StockReturnPicking(models.TransientModel): return super(StockReturnPicking, self).create_returns() def _redirect_to_tukar_guling(self): - """Redirect to Tukar Guling form with pre-filled data""" + """Redirect ke Tukar Guling SO atau PO form dengan pre-filled data""" self.ensure_one() picking = self.picking_id - # if picking.picking_type_id.id == 30 and picking.linked_manual_bu_out.state == 'done': - # raise UserError("❌ BU/PICK tidak dapat di retur karena BU/OUT Sudah Done") - - # Get valid return lines with better error handling + # Ambil lines valid valid_lines = [] + self.env.cr.execute("SELECT id FROM stock_return_picking_line WHERE wizard_id = %s", (self.id,)) + line_ids = [row[0] for row in self.env.cr.fetchall()] + if line_ids: + existing_lines = self.env['stock.return.picking.line'].sudo().browse(line_ids) + for line in existing_lines: + if line.exists() and line.quantity > 0: + valid_lines.append(line) - try: - # Refresh the recordset to ensure we have the latest data - self.env.cr.execute("SELECT id FROM stock_return_picking_line WHERE wizard_id = %s", (self.id,)) - line_ids = [row[0] for row in self.env.cr.fetchall()] - - if line_ids: - # Use sudo to avoid access rights issues and browse existing lines - existing_lines = self.env['stock.return.picking.line'].sudo().browse(line_ids) - for line in existing_lines: - if line.exists() and line.quantity > 0: - valid_lines.append(line) - - # If no lines found via direct query, try the original approach - if not valid_lines: - for line in self.product_return_moves: - if hasattr(line, 'quantity') and line.quantity > 0: - # Additional check to ensure the line is valid - if line.product_id and line.move_id: - valid_lines.append(line) - - except Exception as e: - # Fallback: create lines based on picking moves - valid_lines = [] - for move in picking.move_ids_without_package: - if move.product_uom_qty > 0 and move.state == 'done': - # Create a temporary line object for data extraction - temp_line = type('TempLine', (), { - 'product_id': move.product_id, - 'quantity': move.quantity_done or move.product_uom_qty, - 'move_id': move - })() - valid_lines.append(temp_line) + if not valid_lines: + for line in self.product_return_moves: + if hasattr(line, 'quantity') and line.quantity > 0: + valid_lines.append(line) if not valid_lines: raise UserError(_("Tidak ada produk yang bisa diretur. Pastikan ada produk dengan quantity > 0.")) - # Prepare context for Tukar Guling form + # Siapkan context context = { 'default_operations': picking.id, - # 'default_return_type': 'tukar_guling', 'default_date': fields.Datetime.now(), 'default_state': 'draft', 'default_notes': _('Retur dari %s') % picking.name, - 'from_return_picking': True, # Flag to prevent onchange from overriding lines + 'from_return_picking': True, } - - # Set origin if picking.origin: context['default_origin'] = picking.origin - - # Set partner if picking.partner_id: context['default_partner_id'] = picking.partner_id.id - - # Set shipping address if hasattr(picking, 'real_shipping_id') and picking.real_shipping_id: context['default_real_shipping_id'] = picking.real_shipping_id.id elif picking.partner_id: context['default_real_shipping_id'] = picking.partner_id.id - # Prepare product lines + # Siapkan product lines line_vals = [] sequence = 10 - for line in valid_lines: - try: - # Get quantity - handle both real lines and temp objects - quantity = getattr(line, 'quantity', 0) - if quantity <= 0: - continue - - # Get product - product = getattr(line, 'product_id', None) - if not product: - continue - - line_vals.append((0, 0, { - 'sequence': sequence, - 'product_id': product.id, - 'product_uom_qty': quantity, - 'product_uom': product.uom_id.id, - 'name': product.display_name, - })) - sequence += 10 - - except Exception as e: - # Skip problematic lines + quantity = getattr(line, 'quantity', 0) + if quantity <= 0: continue - + product = getattr(line, 'product_id', None) + if not product: + continue + line_vals.append((0, 0, { + 'sequence': sequence, + 'product_id': product.id, + 'product_uom_qty': quantity, + 'product_uom': product.uom_id.id, + 'name': product.display_name, + })) + sequence += 10 if line_vals: context['default_line_ids'] = line_vals - return { - 'name': _('Tukar Guling'), - 'type': 'ir.actions.act_window', - 'res_model': 'tukar.guling', - 'view_mode': 'form', - 'target': 'current', - 'context': context, - } + if picking.purchase_id or 'PO' in picking.origin: + _logger.info("Redirect ke Tukar Guling PO via purchase_id / origin") + return { + 'name': _('Tukar Guling PO'), + 'type': 'ir.actions.act_window', + 'res_model': 'tukar.guling.po', + 'view_mode': 'form', + 'target': 'current', + 'context': context, + } + else: + _logger.info("This picking is NOT from a PO, fallback to SO.") + return { + 'name': _('Tukar Guling SO'), + 'type': 'ir.actions.act_window', + 'res_model': 'tukar.guling', + 'view_mode': 'form', + 'target': 'current', + 'context': context, + } class ReturnPickingLine(models.TransientModel): 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") diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py new file mode 100644 index 00000000..4ed363cf --- /dev/null +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -0,0 +1,485 @@ +from email.policy import default + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError, ValidationError +import logging + +_logger = logging.getLogger(__name__) + +class TukarGulingPO(models.Model): + _name = 'tukar.guling.po' + _description = 'Tukar Guling PO' + + origin = fields.Char(string='Origin PO') + is_po = fields.Boolean('Is PO', default=True) + is_so = fields.Boolean('Is SO', default=False) + name = fields.Char(string='Name', required=True) + po_picking_ids = fields.One2many( + 'stock.picking', + 'tukar_guling_po_id', + string='Picking Reference', + ) + 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', + string='Operations', + domain=[ + ('picking_type_id.id', 'in', [75, 32]), + ('state', '=', 'done') + ],help='Nomor BU INPUT atau BU PUT' + ) + ba_num = fields.Char('Nomor BA') + return_type = fields.Selection([ + ('revisi_po', 'Revisi PO'), + ('tukar_guling', 'Tukar Guling'), + ], string='Return Type', required=True) + notes = fields.Text('Notes') + tukar_guling_po_id = fields.Many2one('tukar.guling.po', string='Tukar Guling PO', ondelete='cascade') + line_ids = fields.One2many('tukar.guling.line.po', 'tukar_guling_po_id', string='Product Lines') + state = fields.Selection([ + ('draft', 'Draft'), + ('approval_purchase', 'Approval Purchasing'), + ('approval_logistic', 'Approval Logistic'), + ('approval_finance', 'Approval Finance'), + ('done', 'Done'), + ('cancel', 'Cancel'), + ], string='Status', default='draft') + + @api.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.po')], 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.po') or 'embo==' + + # 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(TukarGulingPO, self).create(vals) + + @api.onchange('operations') + def _onchange_operations(self): + """Auto-populate lines ketika operations dipilih""" + if self.operations: + 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 + 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 + + 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, 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() + if not self.operations: + raise UserError("Pilih BU/OUT atau BU/PICK 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_po', 'tukar_guling'] and not record.operations: + raise ValidationError("Operations 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 + + def _is_already_returned(self, picking): + return self.env['stock.picking'].search_count([ + ('origin', '=', 'Return of %s' % picking.name), + ('state', '!=', 'cancel') + ]) > 0 + + def copy(self, default=None): + if default is None: + default = {} + + # Generate new sequence untuk duplicate + sequence = self.env['ir.sequence'].search([('code', '=', 'tukar.guling.po')], limit=1) + if sequence: + default['name'] = sequence.next_by_id() + else: + default['name'] = self.env['ir.sequence'].next_by_code('tukar.guling.po') or 'copy' + + default.update({ + 'state': 'draft', + '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 write(self, vals): + if self.operations.picking_type_id.id != 32: + 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: + vals['origin'] = picking.origin + + return super(TukarGulingPO, self).write(vals) + + def action_view_picking(self): + self.ensure_one() + action = self.env.ref('stock.action_picking_tree_all').read()[0] + pickings = self.po_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: + 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() + picking = self.operations + if picking.picking_type_id.id == 75: + if picking.state != 'done': + raise UserError("BU/PUT belum Done!") + elif picking.picking_type_id.id == 32: + linked_bu_out = picking.linked_manual_bu_out + if linked_bu_out and linked_bu_out.state == 'done': + raise UserError("❌ Tidak bisa retur BU/INPUT karena BU/PUT suda Done!") + if picking.picking_type_id.id != 75 or picking.picking_type_id.id != 32: + if self._is_already_returned(self.operations): + raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + self._validate_product_lines() + + + if self.state != 'draft': + raise UserError("Submit hanya bisa dilakukan dari Draft.") + self.state = 'approval_purchase' + + def action_approve(self): + self.ensure_one() + self._validate_product_lines() + + if not self.operations: + raise UserError("Operations harus diisi!") + + if not self.return_type: + raise UserError("Return Type harus diisi!") + + # Cek hak akses berdasarkan state + for rec in self: + if rec.state == 'approval_purchase': + 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): + for record in self: + if not record.operations: + raise UserError("BU/OUT dari field operations tidak ditemukan.") + + related_pickings = self.env['stock.picking'].search([ + ('origin', '=', record.origin), + ('state', '=', 'done'), + ('picking_type_id', 'in', [75, 32]) + ]) + 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_input_to_return = False + if record.operations.purchase_id: + bu_input_to_return = record.operations.purchase_id.picking_ids.filtered( + lambda p: p.picking_type_id.id == 75 and p.state == 'done' + ) + if bu_input_to_return: + bu_input_to_return = bu_input_to_return[0] + + # BU PUT = operations + bu_put_to_return = record.operations + + if not bu_input_to_return and not bu_put_to_return: + raise UserError("Tidak ada BU INPUT atau BU PUT yang siap diretur.") + + created_returns = [] + + # Lokasi default untuk retur + vrt_type = self.env['stock.picking.type'].browse(77) + prt_type = self.env['stock.picking.type'].browse(76) + bu_input_type = self.env['stock.picking.type'].browse(32) + bu_put_type = self.env['stock.picking.type'].browse(75) + + 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 + + PARTNER_LOCATION_ID = 5 + # BU_OUTPUT_LOCATION_ID = 60 + BU_INPUT_LOCATION_ID = 60 + BU_STOCK_LOCATION_ID = 57 + + # Determine locations based on picking type + if picking.picking_type_id.id == 77: + return_type = vrt_type + default_location_id = BU_STOCK_LOCATION_ID + default_location_dest_id = BU_INPUT_LOCATION_ID + elif picking.picking_type_id.id == 76: + return_type = prt_type + default_location_id = BU_INPUT_LOCATION_ID + default_location_dest_id = PARTNER_LOCATION_ID + elif picking.picking_type_id.id == 75: + return_type = bu_put_type + default_location_id = BU_INPUT_LOCATION_ID + default_location_dest_id = BU_STOCK_LOCATION_ID + elif picking.picking_type_id.id == 32: + return_type = bu_input_type + default_location_id = PARTNER_LOCATION_ID + default_location_dest_id = BU_INPUT_LOCATION_ID + 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, + }) + + 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 + }) + + # Create return lines + return_lines = [] + 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': line.product_id.id, + 'quantity': line.product_uom_qty, + 'move_id': move.id, + })) + if not move: + raise UserError("eror woi") + 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) + 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: + 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, + 'group_id': grup.id, + 'tukar_guling_po_id': record.id, + }) + + return return_picking + + if record.operations.picking_type_id.id == 76: + prt = _create_return_from_picking(record.operations) + if prt: + created_returns.append(prt) + else: + # CASE: Retur dari BU/OUT + vrt = _create_return_from_picking(bu_put_to_return) + if vrt: + created_returns.append(vrt) + + prt = None + if bu_input_to_return: + prt = _create_return_from_picking(bu_input_to_return) + if prt: + created_returns.append(prt) + + if record.return_type == 'tukar_guling': + if vrt: + bu_put = _create_return_from_picking(vrt) + if bu_put: + created_returns.append(bu_put) + + if prt: + bu_input = _create_return_from_picking(prt) + if bu_input: + created_returns.append(bu_input) + + if not created_returns: + raise UserError("wkwkwk") + + + +class TukarGulingLinePO(models.Model): + _name = 'tukar.guling.line.po' + _description = 'Tukar Guling PO Line' + + sequence = fields.Integer('Sequence', default=10, copy=False) + product_id = fields.Many2one('product.product', string='Product', required=True) + tukar_guling_po_id = fields.Many2one('tukar.guling.po', string='Tukar Guling PO', ondelete='cascade') + 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') + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + tukar_guling_po_id = fields.Many2one('tukar.guling.po', string='Tukar Guling PO Ref') \ No newline at end of file diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index a6d0acaa..c2b0895d 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -184,3 +184,5 @@ access_image_carousel,access.image.carousel,model_image_carousel,,1,1,1,1 access_v_sale_notin_matchpo,access.v.sale.notin.matchpo,model_v_sale_notin_matchpo,,1,1,1,1 access_tukar_guling_all_users,tukar.guling.all.users,model_tukar_guling,base.group_user,1,1,1,1 access_tukar_guling_line_all_users,tukar.guling.line.all.users,model_tukar_guling_line,base.group_user,1,1,1,1 +access_tukar_guling_po_all_users,tukar.guling.po.all.users,model_tukar_guling_po,base.group_user,1,1,1,1 +access_tukar_guling_line_po_all_users,tukar.guling.line.po.all.users,model_tukar_guling_line_po,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index 2868f79d..17c9dd8c 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -198,5 +198,13 @@ 1 1 + + Pengajuan Return PO + tukar.guling.po + CCM-po/ + 5 + 1 + 1 + \ No newline at end of file diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index 633f1da4..01721b43 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -10,12 +10,12 @@ + id="menu_pengajuan_tukar_guling" + name="Pengajuan Return SO" + parent="sale.menu_sale_report" + sequence="3" + action="action_pengajuan_tukar_guling" + /> pengajuan.tukar.guling.tree @@ -28,10 +28,10 @@ + decoration-info="state in ('draft', 'approval_sales', 'approval_logistic','approval_finance')" + decoration-success="state == 'done'" + decoration-muted="state == 'cancel'" + /> @@ -43,19 +43,19 @@
@@ -78,15 +78,14 @@ - - + + - + @@ -100,11 +99,11 @@ + options="{'no_create': True, 'no_create_edit': True}"/> + options="{'no_create': True, 'no_create_edit': True}"/> diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml index 76d85904..6e13eee2 100644 --- a/indoteknik_custom/views/tukar_guling_po.xml +++ b/indoteknik_custom/views/tukar_guling_po.xml @@ -3,31 +3,19 @@ - Pengajuan Tukar Guling PO + Pengajuan Return PO ir.actions.act_window tukar.guling.po tree,form - + - - - - Pengajuan Tukar Guling PO - tukar.guling.po - PTGPO/ - 5 - 1 - 1 - - - pengajuan.tukar.guling.po.tree @@ -36,18 +24,17 @@ - + - pengajuan.tukar.guling.po.form @@ -56,55 +43,67 @@
+
+ +

-
- + + + + + + - - - + + + + options="{'no_create': True, 'no_create_edit': True}"/> + options="{'no_create': True, 'no_create_edit': True}"/> @@ -114,4 +113,4 @@
- + \ No newline at end of file -- cgit v1.2.3