stock_assign_serial_numbers.py 4.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from collections import Counter
  4. from odoo import _, api, fields, models
  5. from odoo.exceptions import UserError
  6. class StockAssignSerialNumbers(models.TransientModel):
  7. _inherit = 'stock.assign.serial'
  8. production_id = fields.Many2one('mrp.production', 'Production')
  9. expected_qty = fields.Float('Expected Quantity', digits='Product Unit of Measure')
  10. serial_numbers = fields.Text('Produced Serial Numbers')
  11. produced_qty = fields.Float('Produced Quantity', digits='Product Unit of Measure')
  12. show_apply = fields.Boolean() # Technical field to show the Apply button
  13. show_backorders = fields.Boolean() # Technical field to show the Create Backorder and No Backorder buttons
  14. multiple_lot_components_names = fields.Text() # Names of components with multiple lots, used to show warning
  15. def generate_serial_numbers_production(self):
  16. if self.next_serial_number and self.next_serial_count:
  17. generated_serial_numbers = "\n".join(self.env['stock.lot'].generate_lot_names(self.next_serial_number, self.next_serial_count))
  18. self.serial_numbers = "\n".join([self.serial_numbers, generated_serial_numbers]) if self.serial_numbers else generated_serial_numbers
  19. self._onchange_serial_numbers()
  20. action = self.env["ir.actions.actions"]._for_xml_id("mrp.act_assign_serial_numbers_production")
  21. action['res_id'] = self.id
  22. return action
  23. def _get_serial_numbers(self):
  24. if self.serial_numbers:
  25. return list(filter(lambda serial_number: len(serial_number.strip()) > 0, self.serial_numbers.split('\n')))
  26. return []
  27. @api.onchange('serial_numbers')
  28. def _onchange_serial_numbers(self):
  29. self.show_apply = False
  30. self.show_backorders = False
  31. serial_numbers = self._get_serial_numbers()
  32. duplicate_serial_numbers = [serial_number for serial_number, counter in Counter(serial_numbers).items() if counter > 1]
  33. if duplicate_serial_numbers:
  34. self.serial_numbers = ""
  35. self.produced_qty = 0
  36. raise UserError(_('Duplicate Serial Numbers (%s)') % ','.join(duplicate_serial_numbers))
  37. existing_serial_numbers = self.env['stock.lot'].search([
  38. ('company_id', '=', self.production_id.company_id.id),
  39. ('product_id', '=', self.production_id.product_id.id),
  40. ('name', 'in', serial_numbers),
  41. ])
  42. if existing_serial_numbers:
  43. self.serial_numbers = ""
  44. self.produced_qty = 0
  45. raise UserError(_('Existing Serial Numbers (%s)') % ','.join(existing_serial_numbers.mapped('display_name')))
  46. if len(serial_numbers) > self.expected_qty:
  47. self.serial_numbers = ""
  48. self.produced_qty = 0
  49. raise UserError(_('There are more Serial Numbers than the Quantity to Produce'))
  50. self.produced_qty = len(serial_numbers)
  51. self.show_apply = self.produced_qty == self.expected_qty
  52. self.show_backorders = self.produced_qty > 0 and self.produced_qty < self.expected_qty
  53. def _assign_serial_numbers(self, cancel_remaining_quantity=False):
  54. serial_numbers = self._get_serial_numbers()
  55. productions = self.production_id._split_productions(
  56. {self.production_id: [1] * len(serial_numbers)}, cancel_remaining_quantity, set_consumed_qty=True)
  57. production_lots_vals = []
  58. for serial_name in serial_numbers:
  59. production_lots_vals.append({
  60. 'product_id': self.production_id.product_id.id,
  61. 'company_id': self.production_id.company_id.id,
  62. 'name': serial_name,
  63. })
  64. production_lots = self.env['stock.lot'].create(production_lots_vals)
  65. for production, production_lot in zip(productions, production_lots):
  66. production.lot_producing_id = production_lot.id
  67. production.qty_producing = production.product_qty
  68. for workorder in production.workorder_ids:
  69. workorder.qty_produced = workorder.qty_producing
  70. if productions and len(production_lots) < len(productions):
  71. productions[-1].move_raw_ids.move_line_ids.write({'qty_done': 0})
  72. productions[-1].state = "confirmed"
  73. def apply(self):
  74. self._assign_serial_numbers()
  75. def create_backorder(self):
  76. self._assign_serial_numbers(False)
  77. def no_backorder(self):
  78. self._assign_serial_numbers(True)