diff options
Diffstat (limited to 'indoteknik_custom/models')
| -rwxr-xr-x | indoteknik_custom/models/__init__.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/barcoding_product.py | 37 | ||||
| -rwxr-xr-x | indoteknik_custom/models/product_template.py | 2 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_move.py | 4 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 261 |
5 files changed, 256 insertions, 49 deletions
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 76e4611c..ddbe16f1 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -141,3 +141,4 @@ from . import va_multi_reject from . import stock_immediate_transfer from . import coretax_fatur from . import ir_actions_report +from . import barcoding_product diff --git a/indoteknik_custom/models/barcoding_product.py b/indoteknik_custom/models/barcoding_product.py new file mode 100644 index 00000000..6bbf9fde --- /dev/null +++ b/indoteknik_custom/models/barcoding_product.py @@ -0,0 +1,37 @@ +from odoo import models, api, fields +from odoo.exceptions import AccessError, UserError, ValidationError +from datetime import timedelta, date, datetime +import logging + +_logger = logging.getLogger(__name__) + +class BarcodingProduct(models.Model): + _name = "barcoding.product" + _description = "Barcoding Product" + + barcoding_product_line = fields.One2many('barcoding.product.line', 'barcoding_product_id', string='Barcoding Product Lines', auto_join=True) + product_id = fields.Many2one('product.product', string="Product", tracking=3) + quantity = fields.Float(string="Quantity", tracking=3) + + @api.onchange('product_id', 'quantity') + def _onchange_product_or_quantity(self): + """Update barcoding_product_line based on product_id and quantity""" + if self.product_id and self.quantity > 0: + # Clear existing lines + self.barcoding_product_line = [(5, 0, 0)] + + # Add a new line with the current product and quantity + self.barcoding_product_line = [(0, 0, { + 'product_id': self.product_id.id, + 'barcoding_product_id': self.id, + }) for _ in range(int(self.quantity))] + + +class BarcodingProductLine(models.Model): + _name = 'barcoding.product.line' + _description = 'Barcoding Product Line' + _order = 'barcoding_product_id, id' + + barcoding_product_id = fields.Many2one('barcoding.product', string='Barcoding Product Ref', required=True, ondelete='cascade', index=True, copy=False) + product_id = fields.Many2one('product.product', string="Product") + qr_code_variant = fields.Binary("QR Code Variant", related='product_id.qr_code_variant')
\ No newline at end of file diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 5bedae13..29608297 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -416,7 +416,7 @@ class ProductProduct(models.Model): box_size=5, border=4, ) - qr.add_data(rec.default_code) + qr.add_data(rec.barcode if rec.barcode else rec.default_code) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index e1d4e74c..6b631713 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -12,9 +12,13 @@ class StockMove(models.Model): default=lambda self: self.product_id.print_barcode, ) qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') + barcode = fields.Char(string='Barcode', related='product_id.barcode') def _compute_qr_code_variant(self): for rec in self: + if rec.picking_id.picking_type_code == 'outgoing' and rec.picking_id and rec.picking_id.origin and rec.picking_id.origin.startswith('SO/'): + rec.qr_code_variant = rec.product_id.qr_code_variant + rec.print_barcode = True if rec.print_barcode and rec.print_barcode == True and rec.product_id and rec.product_id.qr_code_variant: rec.qr_code_variant = rec.product_id.qr_code_variant else: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index cd330aeb..cc86c451 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -16,7 +16,8 @@ _logger = logging.getLogger(__name__) class StockPicking(models.Model): _inherit = 'stock.picking' - # check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) + check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) + barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') account_id = fields.Many2one('account.account', string='Account') efaktur_id = fields.Many2one('vit.efaktur', string='Faktur Pajak') @@ -176,7 +177,8 @@ class StockPicking(models.Model): pickings = self.env['stock.picking'].search([ ('picking_type_code', '=', 'outgoing'), ('state', '=', 'done'), - ('carrier_id', '=', 9) + ('carrier_id', '=', 9), + ('lalamove_order_id', '!=', False) ]) for picking in pickings: try: @@ -810,7 +812,24 @@ class StockPicking(models.Model): self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' return res - + + + def check_qty_done_stock(self): + for line in self.move_line_ids_without_package: + def check_qty_per_inventory(self, product, location): + quant = self.env['stock.quant'].search([ + ('product_id', '=', product.id), + ('location_id', '=', location.id), + ]) + + if quant: + return quant.quantity + + return 0 + + qty_onhand = check_qty_per_inventory(self, line.product_id, line.location_id) + if line.qty_done > qty_onhand: + raise UserError('Quantity Done melebihi Quantity Onhand') def send_mail_bills(self): if self.picking_type_code == 'incoming' and self.purchase_id: @@ -875,10 +894,39 @@ class StockPicking(models.Model): @api.model def create(self, vals): self._use_faktur(vals) + if vals.get('picking_type_code') == 'incoming' and vals.get('location_dest_id') == 58: + if 'name' in vals and vals['name'].startswith('BU/IN/'): + vals['name'] = vals['name'].replace('BU/IN/', 'BU/INPUT/', 1) + + if vals.get('picking_type_code') == 'internal' and vals.get('location_id') == 58: + if 'name' in vals and vals['name'].startswith('BU/INT'): + new_name = vals['name'].replace('BU/INT', 'BU/IN', 1) + # Periksa apakah nama sudah ada + if self.env['stock.picking'].search_count([('name', '=', new_name), ('company_id', '=', vals.get('company_id'))]) > 0: + new_name = f"{new_name}-DUP" + vals['name'] = new_name return super(StockPicking, self).create(vals) def write(self, vals): self._use_faktur(vals) + for picking in self: + # Periksa apakah kondisi terpenuhi saat data diubah + if (vals.get('picking_type_code', picking.picking_type_code) == 'incoming' and + vals.get('location_dest_id', picking.location_dest_id.id) == 58): + if 'name' in vals or picking.name.startswith('BU/IN/'): + name_to_modify = vals.get('name', picking.name) + if name_to_modify.startswith('BU/IN/'): + vals['name'] = name_to_modify.replace('BU/IN/', 'BU/INPUT/', 1) + + if (vals.get('picking_type_code', picking.picking_type_code) == 'internal' and + vals.get('location_id', picking.location_id.id) == 58): + name_to_modify = vals.get('name', picking.name) + if name_to_modify.startswith('BU/INT'): + new_name = name_to_modify.replace('BU/INT', 'BU/IN', 1) + # Periksa apakah nama sudah ada + if self.env['stock.picking'].search_count([('name', '=', new_name), ('company_id', '=', picking.company_id.id)]) > 0: + new_name = f"{new_name}-DUP" + vals['name'] = new_name return super(StockPicking, self).write(vals) def _use_faktur(self, vals): @@ -1008,48 +1056,165 @@ class StockPicking(models.Model): return f'{formatted_fastest_eta} - {formatted_longest_eta}' -# class CheckProduct(models.Model): -# _name = 'check.product' -# _description = 'Check Product' -# _order = 'picking_id, id' - -# picking_id = fields.Many2one('stock.picking', string='Picking Reference', required=True, ondelete='cascade', index=True, copy=False) -# product_id = fields.Many2one('product.product', string='Product') - - -# @api.constrains('product_id') -# def check_product_validity(self): -# """ -# Validate if the product exists in the related stock.picking's move_ids_without_package -# and ensure that the product's quantity does not exceed the available product_uom_qty. -# """ -# for record in self: -# if not record.picking_id or not record.product_id: -# continue - -# # Filter move lines in the related picking for the selected product -# moves = record.picking_id.move_ids_without_package.filtered( -# lambda move: move.product_id.id == record.product_id.id -# ) - -# if not moves: -# raise UserError(( -# "The product '%s' is not available in the related stock picking's moves. " -# "Please check and try again." -# ) % record.product_id.display_name) - -# # Calculate the total entries for the product in check.product for the same picking -# product_entries_count = self.search_count([ -# ('picking_id', '=', record.picking_id.id), -# ('product_id', '=', record.product_id.id) -# ]) - -# # Sum the product_uom_qty for all relevant moves -# total_qty_in_moves = sum(moves.mapped('product_uom_qty')) - -# # Compare the count of entries against the available quantity -# if product_entries_count > total_qty_in_moves: -# raise UserError(( -# "The product '%s' exceeds the allowable quantity (%s) in the related stock picking's moves. " -# "You can only add it %s times." -# ) % (record.product_id.display_name, total_qty_in_moves, total_qty_in_moves)) +class CheckProduct(models.Model): + _name = 'check.product' + _description = 'Check Product' + _order = 'picking_id, id' + + picking_id = fields.Many2one( + 'stock.picking', + string='Picking Reference', + required=True, + ondelete='cascade', + index=True, + copy=False, + ) + product_id = fields.Many2one('product.product', string='Product', required=True) + quantity = fields.Float(string='Quantity', default=1.0, required=True) + status = fields.Char(string='Status', compute='_compute_status') + + @api.depends('quantity') + def _compute_status(self): + for record in self: + moves = record.picking_id.move_ids_without_package.filtered( + lambda move: move.product_id.id == record.product_id.id + ) + total_qty_in_moves = sum(moves.mapped('product_uom_qty')) + + if record.quantity < total_qty_in_moves: + record.status = 'Pending' + else: + record.status = 'Done' + + + def create(self, vals): + # Create the record + record = super(CheckProduct, self).create(vals) + # Ensure uniqueness after creation + if not self.env.context.get('skip_consolidate'): + record.with_context(skip_consolidate=True)._consolidate_duplicate_lines() + return record + + def write(self, vals): + # Write changes to the record + result = super(CheckProduct, self).write(vals) + # Ensure uniqueness after writing + if not self.env.context.get('skip_consolidate'): + self.with_context(skip_consolidate=True)._consolidate_duplicate_lines() + return result + + def _sync_check_product_to_moves(self, picking): + """ + Sinkronisasi quantity_done di move_ids_without_package + dengan total quantity dari check.product berdasarkan product_id. + """ + for product_id in picking.check_product_lines.mapped('product_id'): + # Totalkan quantity dari semua baris check.product untuk product_id ini + total_quantity = sum( + line.quantity for line in picking.check_product_lines.filtered(lambda line: line.product_id == product_id) + ) + # Update quantity_done di move yang relevan + moves = picking.move_ids_without_package.filtered(lambda move: move.product_id == product_id) + for move in moves: + move.quantity_done = total_quantity + + def _consolidate_duplicate_lines(self): + """ + Consolidate duplicate lines with the same product_id under the same picking_id + and sync the total quantity to related moves. + """ + for picking in self.mapped('picking_id'): + lines_to_remove = self.env['check.product'] # Recordset untuk menyimpan baris yang akan dihapus + product_lines = picking.check_product_lines.filtered(lambda line: line.product_id) + + # Group lines by product_id + product_groups = {} + for line in product_lines: + product_groups.setdefault(line.product_id.id, []).append(line) + + for product_id, lines in product_groups.items(): + if len(lines) > 1: + # Consolidate duplicate lines + first_line = lines[0] + total_quantity = sum(line.quantity for line in lines) + + # Update the first line's quantity + first_line.with_context(skip_consolidate=True).write({'quantity': total_quantity}) + + # Add the remaining lines to the lines_to_remove recordset + lines_to_remove |= self.env['check.product'].browse([line.id for line in lines[1:]]) + + # Perform unlink after consolidation + if lines_to_remove: + lines_to_remove.unlink() + + # Sync total quantities to moves + self._sync_check_product_to_moves(picking) + + @api.onchange('product_id', 'quantity') + def check_product_validity(self): + for record in self: + if not record.picking_id or not record.product_id: + continue + + # Filter moves related to the selected product + moves = record.picking_id.move_ids_without_package.filtered( + lambda move: move.product_id.id == record.product_id.id + ) + + if not moves: + raise UserError(( + "The product '%s' tidak ada di operations. " + ) % record.product_id.display_name) + + total_qty_in_moves = sum(moves.mapped('product_uom_qty')) + + # Find existing lines for the same product, excluding the current line + existing_lines = record.picking_id.check_product_lines.filtered( + lambda line: line.product_id == record.product_id and line.id != record.id + ) + + if existing_lines: + # Get the first existing line + first_line = existing_lines[0] + + # Calculate the total quantity after addition + total_quantity = sum(existing_lines.mapped('quantity')) - record.quantity + + if total_quantity > total_qty_in_moves: + raise UserError(( + "Quantity Product '%s' sudah melebihi quantity demand." + ) % (record.product_id.display_name)) + else: + # Check if the quantity exceeds the allowed total + if record.quantity > total_qty_in_moves: + raise UserError(( + "Quantity Product '%s' sudah melebihi quantity demand." + ) % (record.product_id.display_name)) + + # Set the quantity to the entered value + record.quantity = record.quantity + +class BarcodeProduct(models.Model): + _name = 'barcode.product' + _description = 'Barcode Product' + _order = 'picking_id, id' + + picking_id = fields.Many2one( + 'stock.picking', + string='Picking Reference', + required=True, + ondelete='cascade', + index=True, + copy=False, + ) + product_id = fields.Many2one('product.product', string='Product', required=True) + barcode = fields.Char(string='Barcode') + + @api.constrains('barcode') + def send_barcode_to_product(self): + for record in self: + if record.barcode and not record.product_id.barcode: + record.product_id.barcode = record.barcode + else: + raise UserError('Barcode sudah terisi')
\ No newline at end of file |
