def _get_next_batch_number(self): max_db = StorableBatch.get_max_batch_number(self.store) max_used = max_value_for(self._get_used_batches() | set([max_db])) if not api.sysparam.get_bool('SYNCHRONIZED_MODE'): return next_value_for(max_used) # On synchronized mode we need to append the branch acronym # to avoid conflicts max_used_list = max_used.split('-') if len(max_used_list) == 1: # '123' max_used = max_used_list[0] elif len(max_used_list) == 2: # '123-AB' max_used = max_used_list[0] else: # TODO: Maybe we should allow only one dash in the batch number # '123-456-AB' max_used = ''.join(max_used_list[:-1]) branch = api.get_current_branch(self.store) if not branch.acronym: raise ValueError("branch '%s' needs an acronym since we are on " "synchronized mode" % (branch.get_description(),)) return '-'.join([next_value_for(max_used), branch.acronym])
def get_order_item(self, sellable, price, quantity, batch=None, parent=None): if parent: if parent.sellable.product.is_package: component = self.get_component(parent, sellable) quantity = parent.quantity * component.quantity price = component.price else: # Do not add the components if its not a package product return if batch is not None: batch = StorableBatch.get_or_create( self.store, storable=sellable.product_storable, batch_number=batch) item = ReturnedSaleItem(store=self.store, quantity=quantity, price=price, sellable=sellable, batch=batch, returned_sale=self.model, parent_item=parent) _adjust_returned_sale_item(item) WizardAddSellableEvent.emit(self.wizard, item) return item
def get_order_item(self, sellable, price, quantity, batch=None, parent=None): if parent: if parent.sellable.product.is_package: component = self.get_component(parent, sellable) quantity = parent.quantity * component.quantity price = component.price else: # Do not add the components if its not a package product return if batch is not None: batch = StorableBatch.get_or_create( self.store, storable=sellable.product_storable, batch_number=batch) item = ReturnedSaleItem( store=self.store, quantity=quantity, price=price, sellable=sellable, batch=batch, returned_sale=self.model, parent_item=parent ) _adjust_returned_sale_item(item) WizardAddSellableEvent.emit(self.wizard, item) return item
def _get_next_batch_number(self): max_db = StorableBatch.get_max_value(self.store, StorableBatch.batch_number) max_used = max_value_for(self._get_used_batches() | set([max_db])) if not api.sysparam.get_bool('SYNCHRONIZED_MODE'): return next_value_for(max_used) # On synchronized mode we need to append the branch acronym # to avoid conflicts max_used_list = max_used.split('-') if len(max_used_list) == 1: # '123' max_used = max_used_list[0] elif len(max_used_list) == 2: # '123-AB' max_used = max_used_list[0] else: # '123-456-AB' max_used = ''.join(max_used_list[:-1]) branch = api.get_current_branch(self.store) if not branch.acronym: raise ValueError("branch '%s' needs an acronym since we are on " "synchronized mode" % (branch.get_description(),)) return '-'.join([next_value_for(max_used), branch.acronym])
def add_sellable(self, sellable, batch_number=None): """Add a sellable in this inventory Note that the :attr:`item's quantity <InventoryItem.recorded_quantity>` will be set based on the registered sellable's stock :param sellable: the |sellable| to be added :param batch_number: a batch number representing a |batch| for the given sellable. It's used like that instead of getting the |batch| directly since we may be adding an item not registered before """ product = sellable.product storable = product.storable if storable is None: raise TypeError("product %r has no storable" % (product, )) if batch_number is not None: batch = StorableBatch.get_or_create(self.store, storable=storable, batch_number=batch_number) quantity = batch.get_balance_for_branch(self.branch) else: batch = None quantity = storable.get_balance_for_branch(self.branch) self.validate_batch(batch, sellable) return InventoryItem(store=self.store, product=sellable.product, batch=batch, product_cost=sellable.cost, recorded_quantity=quantity, inventory=self)
def add_storable(self, storable, quantity, batch_number=None, batch=None): """Add a storable to this inventory. The parameters product, storable and batch are passed here to avoid future queries, increase the performance when opening the inventory :param storable: the |storable| to be added :param quantity: the current quantity of the product in stock :param batch_number: a batch number representing a |batch| for the given sellable. It's used like that instead of getting the |batch| directly since we may be adding an item not registered before :param batch: the corresponding batch to the batch_number """ if batch_number is not None and not batch: batch = StorableBatch.get_or_create(self.store, storable=storable, batch_number=batch_number) product = storable.product sellable = product.sellable self.validate_batch(batch, sellable, storable=storable) return InventoryItem(store=self.store, product=product, batch=batch, product_cost=sellable.cost, recorded_quantity=quantity, inventory=self)
def create_storable_batch(self, storable=None, batch_number=u'1'): from stoqlib.domain.product import StorableBatch if not storable: storable = self.create_storable() return StorableBatch(store=self.store, storable=storable, batch_number=batch_number)
def validate_entry(self, entry): batch_number = str(entry.get_text()) if not batch_number: return available = StorableBatch.is_batch_number_available( self.store, batch_number, exclude_storable=self.model) if (not available or batch_number in self._get_used_batches(exclude=batch_number)): return ValidationError(_("'%s' is already in use") % batch_number)
def validate_entry(self, entry): batch_number = unicode(entry.get_text()) if not batch_number: return available = StorableBatch.is_batch_number_available( self.store, batch_number, exclude_storable=self.model) if (not available or batch_number in self._get_used_batches(exclude=batch_number)): return ValidationError(_("'%s' is already in use") % batch_number)
def add_purchase_item(self, item, quantity=None, batch_number=None, parent_item=None, ipi_value=0, icms_st_value=0): """Add a |purchaseitem| on this receiving order :param item: the |purchaseitem| :param decimal.Decimal quantity: the quantity of that item. If ``None``, it will be get from the item's pending quantity :param batch_number: a batch number that will be used to get or create a |batch| it will be get from the item's pending quantity or ``None`` if the item's |storable| is not controlling batches. :raises: :exc:`ValueError` when validating the quantity and testing the item's order for equality with :obj:`.order` """ pending_quantity = item.get_pending_quantity() if quantity is None: quantity = pending_quantity if not (0 < quantity <= item.quantity): raise ValueError("The quantity must be higher than 0 and lower " "than the purchase item's quantity") if quantity > pending_quantity: raise ValueError("The quantity must be lower than the item's " "pending quantity") sellable = item.sellable storable = sellable.product_storable if batch_number is not None: batch = StorableBatch.get_or_create(self.store, storable=storable, batch_number=batch_number) else: batch = None self.validate_batch(batch, sellable) return ReceivingOrderItem(store=self.store, sellable=item.sellable, batch=batch, quantity=quantity, cost=item.cost, ipi_value=ipi_value, icms_st_value=icms_st_value, purchase_item=item, receiving_order=self, parent_item=parent_item)
def get_order_item(self, sellable, price, quantity, batch=None): if batch is not None: batch = StorableBatch.get_or_create( self.store, storable=sellable.product_storable, batch_number=batch) item = ReturnedSaleItem( store=self.store, quantity=quantity, price=price, sellable=sellable, batch=batch, returned_sale=self.model, ) _adjust_returned_sale_item(item) return item
def add_purchase_item(self, item, quantity=None, batch_number=None, parent_item=None, ipi_value=0, icms_st_value=0): """Add a |purchaseitem| on this receiving order :param item: the |purchaseitem| :param decimal.Decimal quantity: the quantity of that item. If ``None``, it will be get from the item's pending quantity :param batch_number: a batch number that will be used to get or create a |batch| it will be get from the item's pending quantity or ``None`` if the item's |storable| is not controlling batches. :raises: :exc:`ValueError` when validating the quantity and testing the item's order for equality with :obj:`.order` """ pending_quantity = item.get_pending_quantity() if quantity is None: quantity = pending_quantity if not (0 < quantity <= item.quantity): raise ValueError("The quantity must be higher than 0 and lower " "than the purchase item's quantity") if quantity > pending_quantity: raise ValueError("The quantity must be lower than the item's " "pending quantity") sellable = item.sellable storable = sellable.product_storable if batch_number is not None: batch = StorableBatch.get_or_create(self.store, storable=storable, batch_number=batch_number) else: batch = None self.validate_batch(batch, sellable) return ReceivingOrderItem( store=self.store, sellable=item.sellable, batch=batch, quantity=quantity, cost=item.cost, ipi_value=ipi_value, icms_st_value=icms_st_value, purchase_item=item, receiving_order=self, parent_item=parent_item)
def test_is_batch_number_available(self): self.assertTrue(StorableBatch.is_batch_number_available( self.store, u'123')) self.assertTrue(StorableBatch.is_batch_number_available( self.store, u'321')) storable = self.create_storable(is_batch=True) self.create_storable_batch(storable=storable, batch_number=u'123') self.create_storable_batch(batch_number=u'321') # Should not be available anymore since they now exist self.assertFalse(StorableBatch.is_batch_number_available( self.store, u'123')) self.assertFalse(StorableBatch.is_batch_number_available( self.store, u'321')) # But when excluding storable, only u'123' should be available self.assertTrue(StorableBatch.is_batch_number_available( self.store, u'123', exclude_storable=storable)) self.assertFalse(StorableBatch.is_batch_number_available( self.store, u'321', exclude_storable=storable))