summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiqdad <ahmadmiqdad27@gmail.com>2025-07-03 19:55:49 +0700
committerMiqdad <ahmadmiqdad27@gmail.com>2025-07-03 19:55:49 +0700
commitde50bcf16b21abf7b8e45fb59b366c594bc00038 (patch)
treebcf15cf050ea9aac85f574c84c838c0d4c765760
parent90e26bd8d666503c2bb02608e3e5c26f15ab777b (diff)
<miqdad> start tukar guling po
-rwxr-xr-xindoteknik_custom/__manifest__.py2
-rwxr-xr-xindoteknik_custom/models/__init__.py1
-rw-r--r--indoteknik_custom/models/stock_picking_return.py130
-rw-r--r--indoteknik_custom/models/tukar_guling.py14
-rw-r--r--indoteknik_custom/models/tukar_guling_po.py485
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv2
-rw-r--r--indoteknik_custom/views/ir_sequence.xml8
-rw-r--r--indoteknik_custom/views/tukar_guling.xml49
-rw-r--r--indoteknik_custom/views/tukar_guling_po.xml87
9 files changed, 622 insertions, 156 deletions
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 @@
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
+ <record id="seq_tukar_guling" model="ir.sequence">
+ <field name="name">Pengajuan Return PO</field>
+ <field name="code">tukar.guling.po</field>
+ <field name="prefix">CCM-po/</field>
+ <field name="padding">5</field>
+ <field name="number_next">1</field>
+ <field name="number_increment">1</field>
+ </record>
</data>
</odoo> \ 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 @@
</record>
<!-- Menu -->
<menuitem
- id="menu_pengajuan_tukar_guling"
- name="Pengajuan Return SO"
- parent="sale.menu_sale_report"
- sequence="3"
- action="action_pengajuan_tukar_guling"
- />
+ id="menu_pengajuan_tukar_guling"
+ name="Pengajuan Return SO"
+ parent="sale.menu_sale_report"
+ sequence="3"
+ action="action_pengajuan_tukar_guling"
+ />
<!-- Tree View -->
<record id="pengajuan_tukar_guling_tree" model="ir.ui.view">
<field name="name">pengajuan.tukar.guling.tree</field>
@@ -28,10 +28,10 @@
<field name="ba_num" string="Nomor BA"/>
<field name="return_type" string="Return Type"/>
<field name="state" widget="badge"
- decoration-info="state in ('draft', 'approval_sales', 'approval_logistic','approval_finance')"
- decoration-success="state == 'done'"
- decoration-muted="state == 'cancel'"
- />
+ decoration-info="state in ('draft', 'approval_sales', 'approval_logistic','approval_finance')"
+ decoration-success="state == 'done'"
+ decoration-muted="state == 'cancel'"
+ />
</tree>
</field>
</record>
@@ -43,19 +43,19 @@
<form>
<header>
<button name="action_submit" string="Submit" type="object"
- class="btn-primary"
- attrs="{'invisible': [('state', '!=', 'draft')]}"/>
+ class="btn-primary"
+ attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button name="action_approve" string="Approve" type="object"
class="btn-primary"
attrs="{'invisible': [('state', 'not in', ['approval_sales', 'approval_logistic', 'approval_finance'])]}"/>
<button name="action_cancel" string="Cancel" type="object"
- class="btn-secondary"
+ class="btn-secondary"
attrs="{'invisible': [('state', '=', 'draft')]}"
- confirm="Are you sure you want to cancel this record?"/>
+ confirm="Are you sure you want to cancel this record?"/>
<button name="action_draft" string="Set to Draft" type="object"
- class="btn-secondary"
- attrs="{'invisible': [('state', '!=', 'cancel')]}"
- confirm="Are you sure you want to reset this record to draft?"/>
+ class="btn-secondary"
+ attrs="{'invisible': [('state', '!=', 'cancel')]}"
+ confirm="Are you sure you want to reset this record to draft?"/>
<field name="state" widget="statusbar" readonly="1"
statusbar_visible="draft,approval_sales,approval_logistic,approval_finance,done"/>
</header>
@@ -78,15 +78,14 @@
<group>
<field name="date" string="Date" readonly="1"/>
<field name="return_type"/>
-<!-- <field name="ort_num" readonly="1"/>-->
-<!-- <field name="srt_num" readonly="1"/>-->
+ <!-- <field name="ort_num" readonly="1"/>-->
+ <!-- <field name="srt_num" readonly="1"/>-->
<field name="operations" string="Operations"
- attrs="{
- 'invisible': [('return_type', 'not in', ['revisi_so','tukar_guling'])],
- 'required': [('return_type', 'in', ['revisi_so'])]
+ attrs="{
+ 'required': [('return_type', 'in', ['revisi_so', 'tukar_guling'])]
}"/>
<field name="origin" readonly="1"/>
-<!-- <field name="origin_so" readonly="1"/>-->
+ <!-- <field name="origin_so" readonly="1"/>-->
</group>
<group>
<field name="ba_num" string="Nomor BA"/>
@@ -100,11 +99,11 @@
<tree string="Product Lines">
<field name="sequence" widget="handle"/>
<field name="product_id" required="1"
- options="{'no_create': True, 'no_create_edit': True}"/>
+ options="{'no_create': True, 'no_create_edit': True}"/>
<field name="name" force_save="1"/>
<field name="product_uom_qty" string="Quantity"/>
<field name="product_uom" string="UoM"
- options="{'no_create': True, 'no_create_edit': True}"/>
+ options="{'no_create': True, 'no_create_edit': True}"/>
</tree>
</field>
</page>
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 @@
<data>
<!-- Action -->
<record id="action_pengajuan_tukar_guling_po" model="ir.actions.act_window">
- <field name="name">Pengajuan Tukar Guling PO</field>
+ <field name="name">Pengajuan Return PO</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">tukar.guling.po</field>
<field name="view_mode">tree,form</field>
</record>
-
+ <!-- Menu -->
<menuitem
id="menu_pengajuan_tukar_guling_po"
- name="Pengajuan Tukar Guling PO"
+ name="Pengajuan Return PO"
parent="purchase.menu_procurement_management"
- sequence="4"
+ sequence="3"
action="action_pengajuan_tukar_guling_po"
/>
-
- <!-- Sequence -->
- <record id="seq_tukar_guling_po" model="ir.sequence">
- <field name="name">Pengajuan Tukar Guling PO</field>
- <field name="code">tukar.guling.po</field>
- <field name="prefix">PTGPO/</field>
- <field name="padding">5</field>
- <field name="number_next">1</field>
- <field name="number_increment">1</field>
- <field name="company_id" eval="False"/>
- </record>
-
<!-- Tree View -->
<record id="pengajuan_tukar_guling_po_tree" model="ir.ui.view">
<field name="name">pengajuan.tukar.guling.po.tree</field>
@@ -36,18 +24,17 @@
<tree create="1" delete="1" default_order="create_date desc">
<field name="name"/>
<field name="date"/>
- <field name="operations" string="BU/Out"/>
+ <field name="operations" string="Operations"/>
<field name="ba_num" string="Nomor BA"/>
<field name="return_type" string="Return Type"/>
<field name="state" widget="badge"
- decoration-info="state in ('draft', 'approval_purchase', 'approval_logistic','approval_finance')"
- decoration-success="state == 'done'"
- decoration-muted="state == 'cancel'"
+ decoration-info="state in ('draft', 'approval_purchase', 'approval_logistic','approval_finance')"
+ decoration-success="state == 'done'"
+ decoration-muted="state == 'cancel'"
/>
</tree>
</field>
</record>
-
<!-- Form View -->
<record id="pengajuan_tukar_guling_po_form" model="ir.ui.view">
<field name="name">pengajuan.tukar.guling.po.form</field>
@@ -56,55 +43,67 @@
<form>
<header>
<button name="action_submit" string="Submit" type="object"
- class="btn-primary"
- attrs="{'invisible': [('state', '!=', 'draft')]}"/>
+ class="btn-primary"
+ attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button name="action_approve" string="Approve" type="object"
- class="btn-primary"
- attrs="{'invisible': [('state', 'not in', ['approval_purchase', 'approval_logistic', 'approval_finance'])]}"/>
+ class="btn-primary"
+ attrs="{'invisible': [('state', 'not in', ['approval_purchase', 'approval_logistic', 'approval_finance'])]}"/>
<button name="action_cancel" string="Cancel" type="object"
- class="btn-secondary"
- attrs="{'invisible': [('state', '=', 'draft')]}"
- confirm="Are you sure you want to cancel this record?"/>
+ class="btn-secondary"
+ attrs="{'invisible': [('state', '=', 'draft')]}"
+ confirm="Are you sure you want to cancel this record?"/>
<button name="action_draft" string="Set to Draft" type="object"
- class="btn-secondary"
- attrs="{'invisible': [('state', '!=', 'cancel')]}"
- confirm="Are you sure you want to reset this record to draft?"/>
+ class="btn-secondary"
+ attrs="{'invisible': [('state', '!=', 'cancel')]}"
+ confirm="Are you sure you want to reset this record to draft?"/>
<field name="state" widget="statusbar" readonly="1"
- statusbar_visible="draft,approval_purchase,approval_logistic,approval_finance,done"/>
+ statusbar_visible="draft,approval_purchase,approval_logistic,approval_finance,done"/>
</header>
<sheet>
+ <div class="oe_button_box">
+ <button name="action_view_picking"
+ type="object"
+ class="oe_stat_button"
+ icon="fa-truck"
+ attrs="{'invisible': [('po_picking_ids', '=', False)]}">
+ <field name="po_picking_ids" widget="statinfo" string="Delivery"/>
+ </button>
+ </div>
<div class="oe_title">
<h1>
<field name="name" readonly="1" class="oe_inline"/>
</h1>
- <hr/>
</div>
<group>
<group>
<field name="date" string="Date" readonly="1"/>
<field name="return_type"/>
- <field name="operations" string="BU/Out"
- attrs="{
- 'invisible': [('return_type', 'not in', ['revisi_po', 'debit_memo', 'tukar_guling'])],
- 'required': [('return_type', 'in', ['revisi_po', 'debit_memo'])]
- }"/>
+ <!-- <field name="ort_num" readonly="1"/>-->
+ <!-- <field name="srt_num" readonly="1"/>-->
+ <field name="operations" string="Operations"
+ attrs="{
+ 'required': [('return_type', 'in', ['revisi_po', 'tukar_guling'])]
+ }"/>
+ <field name="origin" readonly="1"/>
+ <!-- <field name="origin_so" readonly="1"/>-->
</group>
<group>
<field name="ba_num" string="Nomor BA"/>
<field name="notes"/>
</group>
</group>
+ <!-- Product Lines -->
<notebook>
- <page string="Product Lines" name="product_lines">
- <field name="line_ids">
- <tree string="Product Lines" editable="bottom">
+ <page string="Product Lines" name="product_lines" create="0" edit="0">
+ <field name="line_ids" delete="1" readonly="1">
+ <tree string="Product Lines">
<field name="sequence" widget="handle"/>
<field name="product_id" required="1"
- options="{'no_create': True, 'no_create_edit': True}"/>
+ options="{'no_create': True, 'no_create_edit': True}"/>
<field name="name" force_save="1"/>
<field name="product_uom_qty" string="Quantity"/>
<field name="product_uom" string="UoM"
- options="{'no_create': True, 'no_create_edit': True}"/>
+ options="{'no_create': True, 'no_create_edit': True}"/>
</tree>
</field>
</page>
@@ -114,4 +113,4 @@
</field>
</record>
</data>
-</odoo>
+</odoo> \ No newline at end of file