diff --git a/bahmni_customer_return/models/bahmni_customer_return.py b/bahmni_customer_return/models/bahmni_customer_return.py index 48928cd..f95f230 100644 --- a/bahmni_customer_return/models/bahmni_customer_return.py +++ b/bahmni_customer_return/models/bahmni_customer_return.py @@ -24,7 +24,9 @@ class BahmniCustomerReturn(models.Model): name = fields.Char(string="Return No") entry_date = fields.Datetime('Entry Date',default=fields.Datetime.now) - location_id = fields.Many2one('stock.location', 'Return Location',domain=[('active', '=', True),('usage', '=', 'internal')]) + location_id = fields.Many2one('stock.location', 'Return Location', + default=lambda self: self.env['stock.picking.type'].search([('code', '=', 'incoming'),('sequence_code', '=', 'IN'),('barcode', '=', 'WH-RETURNS')], limit=1).default_location_dest_id.id, + domain=[('active', '=', True),('usage', '=', 'internal')]) customer_id = fields.Many2one('res.partner', 'Customer',domain=[('active', '=', True),('customer_rank', '>', 0)]) status = fields.Selection(selection=CUSTOM_STATUS, string="Status", copy=False, default="draft", readonly=True, store=True, tracking=True) @@ -32,8 +34,10 @@ class BahmniCustomerReturn(models.Model): company_id = fields.Many2one('res.company', copy=False, default=lambda self: self.env.company, ondelete='restrict', readonly=True, required=True) currency_id = fields.Many2one('res.currency', string="Currency", copy=False, default=lambda self: self.env.company.currency_id.id, ondelete='restrict', readonly=True, tracking=True) - tot_amt = fields.Float(string="Return Amount", store=True, compute='_compute_all_line') - product_ids = fields.Many2many('product.product','customer_returns_products','return_id','product_id','Products',domain=[('active', '=', True),('type','=','product')]) + tot_amt = fields.Float(string="Total Amount", store=True, compute='_compute_all_line') + discount_value = fields.Float(string="Dicount Amount", store=True, compute='_compute_all_line') + return_amt = fields.Float(string="Return Amount", store=True, compute='_compute_all_line') + product_ids = fields.Many2many('product.product','customer_returns_products','return_id','product_id','Products',domain=[('active', '=', True)]) active = fields.Boolean(string="Visible", default=True) @@ -108,7 +112,16 @@ def onchange_product_ids(self): @api.depends('line_ids') def _compute_all_line(self): for data in self: - data.tot_amt = sum(line.sub_total for line in data.line_ids) + cumulative_discount_value = 0 + for line in self.line_ids: + total_sale_value_with_tax = line.sale_order_id.amount_untaxed + line.sale_order_id.amount_tax + applied_discount_percentage = (line.sale_order_id.discount / total_sale_value_with_tax) * 100 + line_discount_value = (line.sub_total * applied_discount_percentage) / 100 + cumulative_discount_value += line_discount_value + + data.discount_value = cumulative_discount_value + data.tot_amt = sum(line.sub_total for line in data.line_ids) + data.return_amt = (sum(line.sub_total for line in data.line_ids) - cumulative_discount_value ) def display_warnings(self, warning_msg, kw): @@ -144,9 +157,133 @@ def validations(self, **kw): return self.display_warnings(warning_msg, kw) + @api.model + def auto_return_stock(self): + """Automatically create a stock return picking.""" + + picking_vals = { + 'partner_id': self.customer_id.id, # Customer + 'picking_type_id': self.env['stock.picking.type'].search([('code', '=', 'incoming'),('sequence_code', '=', 'IN'),('barcode', '=', 'WH-RETURNS')], limit=1).id, + 'location_id': self.env['stock.location'].search([('usage', '=', 'customer')], limit=1).id, + 'location_dest_id': self.location_id.id, + 'move_type': 'direct', + 'scheduled_date': time.strftime(TIME_FORMAT), + } + + return_picking = self.env['stock.picking'].create(picking_vals) + + for order in self.line_ids: + + # Create a stock.move entry + move = self.env['stock.move'].create({ + 'name': order.product_id.name, + 'product_id': order.product_id.id, + 'product_uom_qty': order.qty, + 'product_uom': order.sale_order_line_id.product_uom.id, + 'picking_id': return_picking.id, + 'location_id': return_picking.location_id.id, + 'location_dest_id': return_picking.location_dest_id.id, + }) + + # Get the associated outgoing delivery picking + picking = order.sale_order_id.picking_ids.filtered( + lambda p: p.state == 'done' and p.picking_type_id.code == 'outgoing' + ) + if not picking: + raise UserError("No completed delivery order found for this sale order.") + + # Find the move line matching the given lot + if order.product_id.detailed_type =='product' and order.product_id.tracking == 'lot': + move_line = picking.move_line_ids.filtered(lambda ml: ml.lot_id.id == order.lot_id.id) + if not move_line: + raise UserError("The specified lot is not found in the delivery order.") + return_lot_id = order.lot_id.id + else: + return_lot_id = False + + # Create a stock.move.line entry + move_line = self.env['stock.move.line'].create({ + 'move_id': move.id, + 'picking_id': return_picking.id, + 'product_id': order.product_id.id, + 'product_uom_id': order.sale_order_line_id.product_uom.id, + 'qty_done': order.qty, + 'location_id': return_picking.location_id.id, + 'location_dest_id': return_picking.location_dest_id.id, + 'lot_id': return_lot_id, + }) + + ##validate picking + return_picking.with_context(validation_confirmed=True).button_validate() + + + @api.model + def create_partial_credit_note(self): + + # Prepare line items for the credit note + line_items = [] + cumulative_discount_value = 0.00 + for line in self.line_ids: + invoice = line.sale_order_id.invoice_ids.filtered(lambda inv: inv.state == 'posted' and inv.move_type == 'out_invoice') + + if not invoice: + raise ValueError(_("No valid invoice found for the selected Sale Order.")) + + line_items.append((0, 0, { + 'product_id': line.sale_order_line_id.product_id.id, + 'name': line.sale_order_line_id.product_id.display_name or 'Product', + 'quantity': line.qty, + 'price_unit': line.sale_order_line_id.price_unit, + 'account_id': line.sale_order_line_id.product_id.categ_id.property_account_income_categ_id.id or self.env['ir.property']._get('property_account_income_categ_id', 'product.category').id, + })) + if self.discount_value > 0: + line_items.append((0, 0, { + 'product_id': self.env['product.product'].search([('default_code', '=', 'DISC')], limit=1).id, + 'name': self.env['product.product'].search([('default_code', '=', 'DISC')], limit=1).display_name or 'Product', + 'quantity': 1, + 'price_unit': -abs(self.discount_value), + 'account_id': self.env['product.product'].search([('default_code', '=', 'DISC')], limit=1).categ_id.property_account_income_categ_id.id or self.env['ir.property']._get('property_account_income_categ_id', 'product.category').id, + })) + + # Create the credit note + credit_note = self.env['account.move'].create({ + 'move_type': 'out_refund', + 'partner_id': self.customer_id.id, + 'invoice_date': fields.Date.today(), + 'ref': self.name, + 'invoice_line_ids': line_items, + }) + + # Post the credit note + credit_note.action_post() + def entry_confirm(self): if self.status in ('draft'): self.validations() + + for record in self.line_ids: + ### Return Entry Process Start here + # Search for existing return lines for the same sale order line + return_lines = self.env['bahmni.customer.return.line'].search([ + ('sale_order_line_id', '=', record.sale_order_line_id.id)]) + + # Calculate the total return quantity for this sale order line + total_return_qty = sum(line.qty for line in return_lines) + + # Minus the current record's quantity to the total qty + already_done_return_qty = total_return_qty - record.qty + + # Validation: check if total return quantity exceeds the sale order quantity + if total_return_qty > record.sale_order_line_id.product_uom_qty: + raise UserError(f"Sale Order Ref {record.sale_order_id.name} - Already Return quantity {already_done_return_qty} for the product {record.sale_order_line_id.product_id.name} " + f"cannot exceed the total order quantity ({record.sale_order_line_id.product_uom_qty}).") + self.auto_return_stock() + + ### Return Entry Process End + + ### Credit Note Entry Process Start here + self.create_partial_credit_note() + self.name = self.env['ir.sequence'].next_by_code('bahmni.customer.return.sequence') or 'New' self.write({'status': 'confirm', 'confirm_user_id': self.env.user.id, diff --git a/bahmni_customer_return/views/bahmni_customer_return_views.xml b/bahmni_customer_return/views/bahmni_customer_return_views.xml index dffef77..aa11ffa 100644 --- a/bahmni_customer_return/views/bahmni_customer_return_views.xml +++ b/bahmni_customer_return/views/bahmni_customer_return_views.xml @@ -52,7 +52,11 @@

- + +