def add_stock_items(self): """This is normally called from ReceivingOrder when a the receving order is confirmed. """ store = self.store if self.quantity > self.get_remaining_quantity(): raise ValueError( u"Quantity received (%d) is greater than " u"quantity ordered (%d)" % (self.quantity, self.get_remaining_quantity()) ) branch = self.receiving_order.branch storable = self.sellable.product_storable purchase = self.purchase_item.order if storable is not None: storable.increase_stock( self.quantity, branch, StockTransactionHistory.TYPE_RECEIVED_PURCHASE, self.id, self.cost, batch=self.batch, ) purchase.increase_quantity_received(self.purchase_item, self.quantity) ProductHistory.add_received_item(store, branch, self)
def _create_domain(self): self.clean_domain([ProductHistory]) branches = self.store.find(Branch) luvas = self.create_sellable(description=u'Luvas') luvas.code = u'1' botas = self.create_sellable(description=u'Botas') botas.code = u'2' ProductHistory(branch=branches[0], sellable=luvas, quantity_produced=1, production_date=datetime.date.today(), store=self.store) ProductHistory(branch=branches[0], sellable=luvas, quantity_lost=2, production_date=datetime.date(2012, 1, 1), store=self.store) ProductHistory(branch=branches[0], sellable=botas, quantity_lost=3, production_date=datetime.date(2012, 2, 2), store=self.store) ProductHistory(branch=branches[1], sellable=luvas, quantity_produced=3, production_date=datetime.date(2012, 3, 3), store=self.store) ProductHistory(branch=branches[1], sellable=botas, quantity_lost=4, production_date=datetime.date(2012, 4, 4), store=self.store)
def add_lost(self, quantity): """Adds a quantity that was lost. The maximum quantity that can be lost is the total quantity minus the quantity already produced. :param quantity: the quantity that was lost. """ if self.lost + quantity > self.quantity - self.produced: raise ValueError( _(u'Can not lost more items than the total production quantity.' )) store = self.store store.savepoint(u'before_lose') for component in self.get_components(): material = self._get_material_from_component(component) try: material.add_lost(quantity * component.quantity) except ValueError: store.rollback_to_savepoint(u'before_lose') raise self.lost += quantity self.order.try_finalize_production() ProductHistory.add_lost_item(store, self.order.branch, self)
def confirm(self): """Confirms the stock decrease """ assert self.can_confirm() assert self.branch store = self.store branch = self.branch for item in self.get_items(): if item.sellable.product: ProductHistory.add_decreased_item(store, branch, item) item.decrease(branch) old_status = self.status self.status = StockDecrease.STATUS_CONFIRMED # Save the operation_nature and branch in Invoice Table self.invoice.operation_nature = self.operation_nature self.invoice.branch = branch if self.group: self.group.confirm() StockOperationConfirmedEvent.emit(self, old_status)
def add_stock_items(self, user: LoginUser): """This is normally called from ReceivingOrder when a the receving order is confirmed. """ store = self.store if self.quantity > self.get_remaining_quantity(): raise ValueError(u"Quantity received (%d) is greater than " u"quantity ordered (%d)" % (self.quantity, self.get_remaining_quantity())) branch = self.receiving_order.branch storable = self.sellable.product_storable purchase = self.purchase_item.order if storable is not None: cost = self.cost + (self.ipi_value / self.quantity) storable.increase_stock( self.quantity, branch, StockTransactionHistory.TYPE_RECEIVED_PURCHASE, self.id, user, cost, batch=self.batch) purchase.increase_quantity_received(self.purchase_item, self.quantity) ProductHistory.add_received_item(store, branch, self)
def send(self): """Sends this item to it's destination |branch|""" assert self.transfer_order.can_close() storable = self.sellable.product_storable storable.decrease_stock(self.quantity, self.transfer_order.source_branch, StockTransactionHistory.TYPE_TRANSFER_TO, self.id) ProductHistory.add_transfered_item(self.store, self.transfer_order.source_branch, self)
def send_item(self, transfer_item): """Sends a |product| of this order to it's destination |branch|""" assert self.can_close() storable = transfer_item.sellable.product_storable storable.decrease_stock(transfer_item.quantity, self.source_branch, StockTransactionHistory.TYPE_TRANSFER_TO, transfer_item.id) store = self.store ProductHistory.add_transfered_item(store, self.source_branch, transfer_item)
def send(self): """Sends this item to it's destination |branch|. This method should never be used directly, and to send a transfer you should use TransferOrder.send(). """ storable = self.sellable.product_storable storable.decrease_stock(self.quantity, self.transfer_order.source_branch, StockTransactionHistory.TYPE_TRANSFER_TO, self.id, batch=self.batch) ProductHistory.add_transfered_item(self.store, self.transfer_order.source_branch, self)
def produce(self, quantity, produced_by=None, serials=None): """Sets a certain quantity as produced. The quantity will be marked as produced only if there are enough materials allocated, otherwise a ValueError exception will be raised. :param quantity: the quantity that will be produced. """ assert self.can_produce(quantity) # check if its ok to produce before consuming material if self.product.has_quality_tests(): # We have some quality tests to assure. Register it for later assert produced_by assert len(serials) == quantity # We only support yield quantity > 1 when there are no tests assert self.product.yield_quantity == 1 self.store.savepoint(u'before_produce') for component in self.get_components(): material = self._get_material_from_component(component) needed_material = quantity * component.quantity try: material.consume(needed_material) except ValueError: self.store.rollback_to_savepoint(u'before_produce') raise if self.product.has_quality_tests(): for serial in serials: ProductionProducedItem(store=self.store, order=self.order, product=self.product, produced_by=produced_by, produced_date=localnow(), serial_number=serial, entered_stock=False) else: # There are no quality tests for this product. Increase stock # right now. storable = self.product.storable # A production process may yield more than one unit of this product yield_quantity = quantity * self.product.yield_quantity storable.increase_stock( yield_quantity, self.order.branch, StockTransactionHistory.TYPE_PRODUCTION_PRODUCED, self.id) self.produced += quantity self.order.try_finalize_production() ProductHistory.add_produced_item(self.store, self.order.branch, self)
def produce(self, quantity, produced_by=None, serials=None): """Sets a certain quantity as produced. The quantity will be marked as produced only if there are enough materials allocated, otherwise a ValueError exception will be raised. :param quantity: the quantity that will be produced. """ assert self.can_produce(quantity) # check if its ok to produce before consuming material if self.product.has_quality_tests(): # We have some quality tests to assure. Register it for later assert produced_by assert len(serials) == quantity # We only support yield quantity > 1 when there are no tests assert self.product.yield_quantity == 1 self.store.savepoint(u'before_produce') for component in self.get_components(): material = self._get_material_from_component(component) needed_material = quantity * component.quantity try: material.consume(needed_material) except ValueError: self.store.rollback_to_savepoint(u'before_produce') raise if self.product.has_quality_tests(): for serial in serials: ProductionProducedItem(store=self.store, order=self.order, product=self.product, produced_by=produced_by, produced_date=localnow(), serial_number=serial, entered_stock=False) else: # There are no quality tests for this product. Increase stock # right now. storable = self.product.storable # A production process may yield more than one unit of this product yield_quantity = quantity * self.product.yield_quantity storable.increase_stock(yield_quantity, self.order.branch, StockTransactionHistory.TYPE_PRODUCTION_PRODUCED, self.id) self.produced += quantity self.order.try_finalize_production() ProductHistory.add_produced_item(self.store, self.order.branch, self)
def confirm(self): """Confirms the sale """ assert self.can_confirm() assert self.branch store = self.store branch = self.branch for item in self.get_items(): if item.sellable.product: ProductHistory.add_decreased_item(store, branch, item) item.decrease(branch) self.status = StockDecrease.STATUS_CONFIRMED if self.group: self.group.confirm()
def add_lost(self, quantity): """Adds the quantity lost of this material. The maximum quantity that can be lost is given by the formula:: - max_lost(quantity) = needed - consumed - lost - quantity :param quantity: the quantity that was lost. """ assert quantity > 0 if self.lost + quantity > self.needed - self.consumed: raise ValueError(_(u'Cannot loose this quantity.')) required = self.consumed + self.lost + quantity if required > self.allocated: self.allocate(required - self.allocated) self.lost += quantity store = self.store ProductHistory.add_lost_item(store, self.order.branch, self)
def consume(self, quantity): """Consumes a certain quantity of material. The maximum quantity allowed to be consumed is given by the following formula: - max_consumed(quantity) = needed - consumed - lost - quantity :param quantity: the quantity to be consumed. """ assert quantity > 0 available = self.allocated - self.consumed - self.lost if quantity > available: raise ValueError(_(u'Can not consume this quantity.')) required = self.consumed + self.lost + quantity if required > self.allocated: self.allocate(required - self.allocated) self.consumed += quantity store = self.store ProductHistory.add_consumed_item(store, self.order.branch, self)
def consume(self, quantity): """Consumes a certain quantity of material. The maximum quantity allowed to be consumed is given by the following formula: - max_consumed(quantity) = needed - consumed - lost - quantity :param quantity: the quantity to be consumed. """ assert quantity > 0 available = self.allocated - self.consumed - self.lost if quantity > available: raise ValueError(_(u'Can not consume this quantity.')) required = self.consumed + self.lost + quantity if required > self.allocated: # pragma nocover self.allocate(required - self.allocated) self.consumed += quantity store = self.store ProductHistory.add_consumed_item(store, self.order.branch, self)
def add_lost(self, quantity): """Adds a quantity that was lost. The maximum quantity that can be lost is the total quantity minus the quantity already produced. :param quantity: the quantity that was lost. """ if self.lost + quantity > self.quantity - self.produced: raise ValueError( _(u'Can not lost more items than the total production quantity.')) store = self.store store.savepoint(u'before_lose') for component in self.get_components(): material = self._get_material_from_component(component) try: material.add_lost(quantity * component.quantity) except ValueError: store.rollback_to_savepoint(u'before_lose') raise self.lost += quantity self.order.try_finalize_production() ProductHistory.add_lost_item(store, self.order.branch, self)
def apply_patch(store): # Creation of new column in stock_decrease table. # And added new Cfop to cfop_data table. store.execute("""ALTER TABLE stock_decrease ADD COLUMN cfop_id bigint REFERENCES cfop_data(id);""") # Default Cfop should be use in manual stock decrease. cfop_data = store.find(CfopData, code=u'5.949').one() if not cfop_data: cfop_data = CfopData(store=store, code=u"5.949", description=u"Outra saída de mercadoria ou " u"prestação de serviço não " u"especificado") # Adjusting existing manuals outputs for stock_decrease in store.find(StockDecrease): stock_decrease.cfop = cfop_data retentions = store.execute(""" SELECT id, quantity, reason, retention_date, product_id, cfop_id FROM product_retention_history ORDER BY id;""").get_all() # Without retentions, there is no need to create user and employee # variables. if len(retentions): # Default user for migration user = get_admin_user(store) if user is None: users = Person.iselectBy(IUser, is_active=True, store=store).order_by(Person.id) user = users[0] # Default employee for migration employee = IEmployee(user.person, None) if employee is None: employees = Person.iselectBy(IEmployee, is_active=True, store=store).order_by(Person.id) employee = employees[0] default_branch = sysparam().MAIN_COMPANY notes = _(u"Stock decrease imported from old retention.") history = store.execute(""" SELECT id, quantity_retained, sellable_id, branch_id FROM product_history WHERE quantity_retained is not null ORDER BY id;""").get_all() for i in range(len(retentions)): ret = retentions[i] hist = history[i] product = Product.get(ret[4], store=store) branch_id = hist[3] if ret[1] != hist[1] or product.sellable.id != hist[2]: branch_id = default_branch.id decrease = StockDecrease(store=store, confirm_date=ret[3], status=StockDecrease.STATUS_CONFIRMED, reason=ret[2], notes=notes, responsible=user, removed_by=employee, branch_id=branch_id, cfop_id=ret[5]) decrease_item = StockDecreaseItem(store=store, quantity=ret[1], sellable=product.sellable) decrease.add_item(decrease_item) store.remove(hist[0]) ProductHistory(branch_id=branch_id, sellable=product.sellable, quantity_decreased=decrease_item.quantity, decreased_date=decrease.confirm_date, store=store) store.execute("""ALTER TABLE product_history DROP COLUMN quantity_retained;""") store.execute("DROP TABLE product_retention_history;")
def apply_patch(store): # Creation of new column in stock_decrease table. # And added new Cfop to cfop_data table. store.execute("""ALTER TABLE stock_decrease ADD COLUMN cfop_id bigint REFERENCES cfop_data(id);""") # Default Cfop should be use in manual stock decrease. cfop_data = store.find(CfopData, code=u'5.949').one() if not cfop_data: cfop_data = CfopData(store=store, code=u"5.949", description=u"Outra saída de mercadoria ou " u"prestação de serviço não " u"especificado") # Adjusting existing manuals outputs for stock_decrease in store.find(StockDecrease): stock_decrease.cfop = cfop_data retentions = store.execute(""" SELECT id, quantity, reason, retention_date, product_id, cfop_id FROM product_retention_history ORDER BY id;""").get_all() # Without retentions, there is no need to create user and employee # variables. if len(retentions): # Default user for migration user = get_admin_user(store) if user is None: users = Person.iselectBy(IUser, is_active=True, store=store).order_by(Person.id) user = users[0] # Default employee for migration employee = IEmployee(user.person, None) if employee is None: employees = Person.iselectBy(IEmployee, is_active=True, store=store).order_by(Person.id) employee = employees[0] default_branch = sysparam(store).MAIN_COMPANY notes = _(u"Stock decrease imported from old retention.") history = store.execute(""" SELECT id, quantity_retained, sellable_id, branch_id FROM product_history WHERE quantity_retained is not null ORDER BY id;""").get_all() for i in range(len(retentions)): ret = retentions[i] hist = history[i] product = Product.get(ret[4], store=store) branch_id = hist[3] if ret[1] != hist[1] or product.sellable.id != hist[2]: branch_id = default_branch.id decrease = StockDecrease(store=store, confirm_date=ret[3], status=StockDecrease.STATUS_CONFIRMED, reason=ret[2], notes=notes, responsible=user, removed_by=employee, branch_id=branch_id, cfop_id=ret[5]) decrease_item = StockDecreaseItem(store=store, quantity=ret[1], sellable=product.sellable) decrease.add_item(decrease_item) ProductHistory.delete(hist[0], store) ProductHistory(branch_id=branch_id, sellable=product.sellable, quantity_decreased=decrease_item.quantity, decreased_date=decrease.confirm_date, store=store) store.execute("""ALTER TABLE product_history DROP COLUMN quantity_retained;""") store.execute("DROP TABLE product_retention_history;")