def process_one(self, data, fields, store): base_category = self._get_or_create( SellableCategory, store, suggested_markup=Decimal(data.markup), salesperson_commission=Decimal(data.commission), category=None, description=data.base_category) # create a commission source self._get_or_create(CommissionSource, store, direct_value=Decimal(data.commission), installments_value=Decimal(data.commission2), category=base_category) category = self._get_or_create(SellableCategory, store, description=data.category, suggested_markup=Decimal(data.markup2), category=base_category) sellable = Sellable(store=store, cost=Decimal(data.cost), category=category, description=data.description, price=Decimal(data.price)) sellable.barcode = data.barcode sellable.code = u'%02d' % self._code self._code += 1 if u'unit' in fields: if not data.unit in self.units: raise ValueError(u"invalid unit: %s" % data.unit) sellable.unit = store.fetch(self.units[data.unit]) sellable.tax_constant_id = self.tax_constant_id product = Product(store=store, sellable=sellable, ncm=data.ncm) taxes = self._maybe_create_taxes(store) product.icms_template = taxes['icms'] product.pis_template = taxes['pis'] product.cofins_template = taxes['cofins'] supplier = store.fetch(self.supplier) ProductSupplierInfo(store=store, supplier=supplier, is_main_supplier=True, base_cost=Decimal(data.cost), product=product) Storable(product=product, store=store)
def process_one(self, data, fields, store): base_category = self._get_or_create( SellableCategory, store, suggested_markup=Decimal(data.markup), salesperson_commission=Decimal(data.commission), category=None, description=data.base_category) # create a commission source self._get_or_create( CommissionSource, store, direct_value=Decimal(data.commission), installments_value=Decimal(data.commission2), category=base_category) category = self._get_or_create( SellableCategory, store, description=data.category, suggested_markup=Decimal(data.markup2), category=base_category) sellable = Sellable(store=store, cost=Decimal(data.cost), category=category, description=data.description, price=Decimal(data.price)) sellable.barcode = data.barcode sellable.code = u'%02d' % self._code self._code += 1 if u'unit' in fields: if not data.unit in self.units: raise ValueError(u"invalid unit: %s" % data.unit) sellable.unit = store.fetch(self.units[data.unit]) sellable.tax_constant_id = self.tax_constant_id product = Product(store=store, sellable=sellable, ncm=data.ncm) taxes = self._maybe_create_taxes(store) product.icms_template = taxes['icms'] product.pis_template = taxes['pis'] product.cofins_template = taxes['cofins'] supplier = store.fetch(self.supplier) ProductSupplierInfo(store=store, supplier=supplier, is_main_supplier=True, base_cost=Decimal(data.cost), product=product) Storable(product=product, store=store)
def create_model(self, store): self._model_created = True sellable = Sellable(store=store) sellable.tax_constant_id = sysparam.get_object_id('DEFAULT_PRODUCT_TAX_CONSTANT') sellable.unit_id = sysparam.get_object_id('SUGGESTED_UNIT') model = Product(store=store, sellable=sellable) if self._product_type != Product.TYPE_WITHOUT_STOCK: storable = Storable(product=model, store=store) if self._product_type == Product.TYPE_BATCH: storable.is_batch = True elif self._product_type == Product.TYPE_WITHOUT_STOCK: model.manage_stock = False elif self._product_type == Product.TYPE_CONSIGNED: model.consignment = True return model
def create_model(self, store): self._model_created = True tax_constant = sysparam(store).DEFAULT_PRODUCT_TAX_CONSTANT sellable = Sellable(store=store) sellable.tax_constant = tax_constant sellable.unit = sysparam(self.store).SUGGESTED_UNIT model = Product(store=store, sellable=sellable) Storable(product=model, store=store) return model
def _setup_widgets(self): self.product_manufacturer.prefill( api.for_combo(self.store.find(ProductManufacturer))) self.product_brand.prefill([(m, m) for m in sorted( Product.find_distinct_values(self.store, Product.brand))]) self.product_family.prefill([(m, m) for m in sorted( Product.find_distinct_values(self.store, Product.family))]) self.username.set_text(self.model.user.person.name) self.open_time.set_text(self.model.open_date.strftime("%X")) # load categories self.category_tree.set_columns(self._get_columns()) for category in SellableCategory.get_base_categories(self.store): self._append_category(category) self._uncategorized_products = self._append_category( _UncategorizedProductsCategory())
def post(self, store): data = self.get_json() if 'product' not in data: abort(400, 'There is no product data on payload') sellable_id = data.get('sellable_id') barcode = data.get('barcode') description = data.get('description') base_price = self._price_validation(data) if sellable_id and store.get(Sellable, sellable_id): abort(400, 'Product with this id already exists') if barcode and store.find(Sellable, barcode=barcode): abort(400, 'Product with this barcode already exists') sellable = Sellable(store=store) if sellable_id: sellable.id = sellable_id sellable.code = barcode sellable.barcode = barcode sellable.description = description # FIXME The sellable is created with STATUS_CLOSED because we need the taxes info # to start selling so this is just a temporary sellable just to save it on the # database so the override can be created sellable.status = Sellable.STATUS_CLOSED sellable.base_price = base_price product_data = data.get('product') product = Product(store=store, sellable=sellable) product.manage_stock = product_data.get('manage_stock', False) return make_response( jsonify({ 'message': 'Product created', 'data': { 'id': sellable.id, 'barcode': sellable.barcode, 'description': sellable.description, 'status': sellable.status, } }), 201)
def _setup_widgets(self): self.product_manufacturer.prefill( api.for_combo(self.store.find(ProductManufacturer))) self.product_brand.prefill( [(m, m) for m in sorted(Product.find_distinct_values(self.store, Product.brand))]) self.product_family.prefill( [(m, m) for m in sorted(Product.find_distinct_values(self.store, Product.family))]) self.username.set_text(self.model.user.person.name) self.open_time.set_text(self.model.open_date.strftime("%X")) # load categories self.category_tree.set_columns(self._get_columns()) for category in SellableCategory.get_base_categories(self.store): self._append_category(category) self._uncategorized_products = self._append_category( _UncategorizedProductsCategory())
def create_model(self, store): self._model_created = True tax_constant = sysparam(store).DEFAULT_PRODUCT_TAX_CONSTANT sellable = Sellable(store=store) sellable.tax_constant = tax_constant sellable.unit = sysparam(self.store).SUGGESTED_UNIT model = Product(store=store, sellable=sellable) # FIXME: Instead of creating and then removing, we should only create # the Storable if the user chooses to do so, but due to the way the # editor is implemented, it is not that easy. Change this once we write # the new product editor. Storable(product=model, store=store) return model
def create_sellable(self, product_info, responsible): if product_info.get('is_package') != 'true': raise SellableError( """A criação de produtos somente é permitida para produtos do tipo pacote. Verifique o cache do seu navegador.""") sellable = None if product_info["code"]: sellable = self.store.find(Sellable, Sellable.code == product_info["code"]) if sellable: return sellable = Sellable(store=self.store, description=product_info["description"], cost=Decimal(product_info["cost"]), price=Decimal(product_info["price"])) sellable.code = product_info["code"] sellable.barcode = product_info["barcode"] sellable.notes = "Created via API" + product_info["notes"] sellable.unit_id = product_info["unit_id"] or None sellable.tax_constant_id = product_info["tax_constant"] or None sellable.default_sale_cfop_id = product_info[ "default_sale_cfop_id"] or None sellable.category_id = product_info["category_id"] or None # FIXME Need to get more info from NFe to fill both Product and Storable product = Product(store=self.store, sellable=sellable) product.manage_stock = product_info.get('manage_stock') == 'true' product.is_package = product_info.get('is_package') == 'true' package_quantity = product_info.get('package_quantity') item_ean = product_info.get('item_ean') item = self.store.find(Sellable, barcode=item_ean).one() ProductComponent(product=product, component=item.product, price=sellable.get_price(), quantity=Decimal(package_quantity)) return sellable
def create_sellable(self, price=None, product=True, description=u'Description'): from stoqlib.domain.product import Product from stoqlib.domain.service import Service from stoqlib.domain.sellable import Sellable tax_constant = sysparam(self.store).DEFAULT_PRODUCT_TAX_CONSTANT if price is None: price = 10 sellable = Sellable(cost=125, price=price, description=description, store=self.store) sellable.tax_constant = tax_constant if product: Product(sellable=sellable, store=self.store) else: Service(sellable=sellable, store=self.store) return sellable
def process_one(self, data, fields, store): base_category = self._get_or_create(SellableCategory, store, suggested_markup=int(data.markup), salesperson_commission=int( data.commission), category=None, description=data.base_category) # create a commission source self._get_or_create(CommissionSource, store, direct_value=int(data.commission), installments_value=int(data.commission2), category=base_category) category = self._get_or_create(SellableCategory, store, description=data.category, suggested_markup=int(data.markup2), category=base_category) sellable = Sellable(store=store, cost=Decimal(data.cost), category=category, description=data.description, price=int(data.price)) sellable.barcode = sellable.code = data.barcode if u'unit' in fields: if not data.unit in self.units: raise ValueError(u"invalid unit: %s" % data.unit) sellable.unit = store.fetch(self.units[data.unit]) sellable.tax_constant = store.fetch(self.tax_constant) product = Product(sellable=sellable, store=store) supplier = store.fetch(self.supplier) ProductSupplierInfo(store=store, supplier=supplier, is_main_supplier=True, base_cost=Decimal(data.cost), product=product) Storable(product=product, store=store)
def testSell(self): sale = self.create_sale() sellable = Sellable(store=self.store) sellable.barcode = u'xyz' product = Product(sellable=sellable, store=self.store) sale_item = sale.add_sellable(product.sellable) branch = get_current_branch(self.store) storable = self.create_storable(product, branch, 2) stock_item = storable.get_stock_item(branch, None) assert stock_item is not None current_stock = stock_item.quantity if current_stock: storable.decrease_stock(current_stock, branch, 0, 0) assert not storable.get_stock_item(branch, None).quantity sold_qty = 2 storable.increase_stock(sold_qty, branch, 0, 0) assert storable.get_stock_item(branch, None) is not None assert storable.get_stock_item(branch, None).quantity == sold_qty # now setting the proper sold quantity in the sellable item sale_item.quantity = sold_qty sale_item.sell(branch) assert not storable.get_stock_item(branch, None).quantity
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 create_model(self, store): self._model_created = True sellable = Sellable(store=store) model = Product(store=store, sellable=sellable) if self._product_type != Product.TYPE_WITHOUT_STOCK: storable = Storable(product=model, store=store) if self._product_type == Product.TYPE_BATCH: storable.is_batch = True elif self._product_type == Product.TYPE_WITHOUT_STOCK: model.manage_stock = False elif self._product_type == Product.TYPE_CONSIGNED: model.consignment = True elif self._product_type == Product.TYPE_GRID: model.is_grid = True # Configurable products should not manage stock model.manage_stock = False if self._template is not None: sellable.tax_constant = self._template.sellable.tax_constant sellable.unit = self._template.sellable.unit sellable.category = self._template.sellable.category model.manufacturer = self._template.manufacturer model.brand = self._template.brand model.family = self._template.family model.ncm = self._template.ncm model.icms_template = self._template.icms_template model.ipi_template = self._template.ipi_template for product_attr in self._template.attributes: ProductAttribute(store=self.store, product_id=model.id, attribute_id=product_attr.attribute.id) for supplier_info in self._template.suppliers: ProductSupplierInfo(store=self.store, product=model, supplier=supplier_info.supplier) else: sellable.tax_constant_id = sysparam.get_object_id( 'DEFAULT_PRODUCT_TAX_CONSTANT') sellable.unit_id = sysparam.get_object_id('SUGGESTED_UNIT') return model
def setUp(self): DomainTest.setUp(self) sellable = self.create_sellable() self.product = Product(sellable=sellable, store=self.store)
class TestProduct(DomainTest): def setUp(self): DomainTest.setUp(self) sellable = self.create_sellable() self.product = Product(sellable=sellable, store=self.store) def test_get_main_supplier_info(self): self.failIf(self.product.get_main_supplier_info()) supplier = self.create_supplier() ProductSupplierInfo(store=self.store, supplier=supplier, product=self.product, is_main_supplier=True) self.failUnless(self.product.get_main_supplier_info()) def testGetComponents(self): self.assertEqual(list(self.product.get_components()), []) components = [] for i in range(3): component = self.create_product() product_component = ProductComponent(product=self.product, component=component, store=self.store) components.append(product_component) self.assertEqual(list(self.product.get_components()), components) def testHasComponents(self): self.assertFalse(self.product.has_components()) component = self.create_product() ProductComponent(product=self.product, component=component, store=self.store) self.assertTrue(self.product.has_components()) def testGetProductionCost(self): product = self.create_product() sellable = product.sellable sellable.cost = 50 production_cost = sellable.cost self.assertEqual(product.get_production_cost(), production_cost) def testIsComposedBy(self): component = self.create_product() self.assertEqual(self.product.is_composed_by(component), False) ProductComponent(product=self.product, component=component, store=self.store) self.assertEqual(self.product.is_composed_by(component), True) component2 = self.create_product() ProductComponent(product=component, component=component2, store=self.store) self.assertEqual(self.product.is_composed_by(component2), True) self.assertEqual(component.is_composed_by(component2), True) component3 = self.create_product() ProductComponent(product=self.product, component=component3, store=self.store) self.assertEqual(self.product.is_composed_by(component3), True) self.assertEqual(component.is_composed_by(component3), False) self.assertEqual(component2.is_composed_by(component3), False) def testSuppliers(self): product = self.create_product() supplier = self.create_supplier() info = ProductSupplierInfo(store=self.store, product=product, supplier=supplier) suppliers = list(product.get_suppliers_info()) # self.create_product already adds a supplier. so here we must have 2 self.assertEqual(len(suppliers), 2) self.assertEqual(info in suppliers, True) # product.suppliers should behave just like get_suppliers_info() self.assertEqual(len(list(product.suppliers)), 2) self.assertEqual(info in product.suppliers, True) self.assertEqual(product.is_supplied_by(supplier), True) def testCanRemove(self): product = self.create_product() storable = Storable(product=product, store=self.store) self.assertTrue(product.can_remove()) storable.increase_stock(1, get_current_branch(self.store), 0, 0) self.assertFalse(product.can_remove()) # Product was sold. sale = self.create_sale() sale.add_sellable(product.sellable, quantity=1, price=10) method = PaymentMethod.get_by_name(self.store, u'money') method.create_inpayment(sale.group, sale.branch, sale.get_sale_subtotal()) sale.order() sale.confirm() self.assertFalse(product.can_remove()) # Product is a component. from stoqlib.domain.product import ProductComponent product = self.create_product(10) component = self.create_product(5) Storable(product=component, store=self.store) self.assertTrue(component.can_remove()) ProductComponent(product=product, component=component, store=self.store) self.assertFalse(component.can_remove()) # Product is used in a production. from stoqlib.domain.production import ProductionItem product = self.create_product() Storable(product=product, store=self.store) self.assertTrue(product.can_remove()) order = self.create_production_order() ProductionItem(product=product, order=order, quantity=1, store=self.store) self.assertFalse(product.can_remove()) def testRemove(self): product = self.create_product() Storable(product=product, store=self.store) total = self.store.find(Product, id=product.id).count() self.assertEquals(total, 1) product.remove() total = self.store.find(Product, id=product.id).count() self.assertEquals(total, 0) def testIncreaseDecreaseStock(self): branch = get_current_branch(self.store) product = self.create_product() storable = Storable(product=product, store=self.store) stock_item = storable.get_stock_item(branch) self.failIf(stock_item is not None) storable.increase_stock(1, branch, 0, 0) stock_item = storable.get_stock_item(branch) self.assertEquals(stock_item.stock_cost, 0) storable.increase_stock(1, branch, 0, 0, unit_cost=10) stock_item = storable.get_stock_item(branch) self.assertEquals(stock_item.stock_cost, 5) stock_item = storable.decrease_stock(1, branch, 0, 0) self.assertEquals(stock_item.stock_cost, 5) storable.increase_stock(1, branch, 0, 0) stock_item = storable.get_stock_item(branch) self.assertEquals(stock_item.stock_cost, 5) storable.increase_stock(2, branch, 0, 0, unit_cost=15) stock_item = storable.get_stock_item(branch) self.assertEquals(stock_item.stock_cost, 10) def test_lead_time(self): product = self.create_product() Storable(product=product, store=self.store) branch = get_current_branch(self.store) supplier1 = self.create_supplier() ProductSupplierInfo(store=self.store, product=product, supplier=supplier1, lead_time=10) self.assertEqual(product.get_max_lead_time(1, branch), 10) supplier2 = self.create_supplier() ProductSupplierInfo(store=self.store, product=product, supplier=supplier2, lead_time=20) self.assertEqual(product.get_max_lead_time(1, branch), 20) # Now for composed products product = self.create_product(create_supplier=False) product.is_composed = True product.production_time = 5 Storable(product=product, store=self.store) component = self.create_product(create_supplier=False) Storable(product=component, store=self.store) ProductSupplierInfo(store=self.store, product=component, supplier=supplier1, lead_time=7) self.assertEqual(component.get_max_lead_time(1, branch), 7) pc = ProductComponent(product=product, component=component, quantity=1, store=self.store) self.assertEqual(product.get_max_lead_time(1, branch), 12) # Increase the component stock component.storable.increase_stock(1, branch, 0, 0) self.assertEqual(product.get_max_lead_time(1, branch), 5) # Increase the quantity required: pc.quantity = 2 self.assertEqual(product.get_max_lead_time(1, branch), 12)
def testCreateEvent(self): store_list = [] p_data = _ProductEventData() ProductCreateEvent.connect(p_data.on_create) ProductEditEvent.connect(p_data.on_edit) ProductRemoveEvent.connect(p_data.on_delete) try: # Test product being created store = new_store() store_list.append(store) sellable = Sellable( store=store, description=u'Test 1234', price=Decimal(2), ) product = Product( store=store, sellable=sellable, ) store.commit() self.assertTrue(p_data.was_created) self.assertFalse(p_data.was_edited) self.assertFalse(p_data.was_deleted) self.assertEqual(p_data.product, product) p_data.reset() # Test product being edited and emmiting the event just once store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) sellable.notes = u'Notes' sellable.description = u'Test 666' product.weight = Decimal(10) store.commit() self.assertTrue(p_data.was_edited) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_deleted) self.assertEqual(p_data.product, product) self.assertEqual(p_data.emmit_count, 1) p_data.reset() # Test product being edited, editing Sellable store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) sellable.notes = u'Notes for test' store.commit() self.assertTrue(p_data.was_edited) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_deleted) self.assertEqual(p_data.product, product) self.assertEqual(p_data.emmit_count, 1) p_data.reset() # Test product being edited, editing Product itself store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) product.weight = Decimal(1) store.commit() self.assertTrue(p_data.was_edited) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_deleted) self.assertEqual(p_data.product, product) self.assertEqual(p_data.emmit_count, 1) p_data.reset() # Test product being edited, editing Product itself store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) product.weight = Decimal(1) store.commit() #self.assertTrue(p_data.was_edited) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_deleted) #self.assertEqual(p_data.product, product) #self.assertEqual(p_data.emmit_count, 1) p_data.reset() finally: # Test product being removed store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) sellable.remove() store.commit() self.assertTrue(p_data.was_deleted) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_edited) self.assertEqual(p_data.product, product) self.assertEqual(p_data.emmit_count, 1) p_data.reset() for store in store_list: store.close()
def create_model(self, store): self._model_created = True sellable = Sellable(store=store) model = Product(store=store, sellable=sellable) no_storable = [Product.TYPE_WITHOUT_STOCK, Product.TYPE_PACKAGE] if not self._product_type in no_storable: storable = Storable(product=model, store=store) if self._product_type == Product.TYPE_BATCH: storable.is_batch = True elif self._product_type == Product.TYPE_WITHOUT_STOCK: model.manage_stock = False elif self._product_type == Product.TYPE_CONSIGNED: model.consignment = True elif self._product_type == Product.TYPE_GRID: model.is_grid = True # Configurable products should not manage stock model.manage_stock = False elif self._product_type == Product.TYPE_PACKAGE: model.is_package = True # Package products should not manage stock model.manage_stock = False if self._template is not None: sellable.tax_constant = self._template.sellable.tax_constant sellable.unit = self._template.sellable.unit sellable.category = self._template.sellable.category sellable.base_price = self._template.sellable.base_price sellable.cost = self._template.sellable.cost model.manufacturer = self._template.manufacturer model.brand = self._template.brand model.family = self._template.family model.ncm = self._template.ncm model.icms_template = self._template.icms_template model.ipi_template = self._template.ipi_template for product_attr in self._template.attributes: ProductAttribute(store=self.store, product_id=model.id, attribute_id=product_attr.attribute.id) for supplier_info in self._template.suppliers: ProductSupplierInfo( store=self.store, product=model, supplier=supplier_info.supplier) else: sellable.tax_constant_id = sysparam.get_object_id( 'DEFAULT_PRODUCT_TAX_CONSTANT') sellable.unit_id = sysparam.get_object_id('SUGGESTED_UNIT') return model
def post(self, store): data = self.get_json() log.debug("POST /sellable station: %s payload: %s", self.get_current_station(store), data) if 'product' not in data: abort(400, 'There is no product data on payload') sellable_id = data.get('sellable_id') barcode = data.get('barcode') description = data.get('description') base_price = self._price_validation(data) sellable = store.get(Sellable, sellable_id) sellable_created_via_sale = sellable and Sellable.NOTES_CREATED_VIA_SALE in sellable.notes if sellable and not sellable_created_via_sale: message = 'Product with id {} already exists'.format(sellable_id) log.warning(message) return make_response(jsonify({ 'message': message, }), 200) if barcode and store.find(Sellable, barcode=barcode): message = 'Product with barcode {} already exists'.format(barcode) log.warning(message) return make_response(jsonify({ 'message': message, }), 200) if not sellable: sellable = Sellable(store=store) if sellable_id: sellable.id = sellable_id sellable.code = barcode sellable.barcode = barcode sellable.description = description # FIXME The sellable is created with STATUS_CLOSED because we need the taxes info # to start selling so this is just a temporary sellable just to save it on the # database so the override can be created sellable.status = Sellable.STATUS_CLOSED sellable.base_price = base_price # If the sellable was pre-created on a sale it has a notes informing it and to # proceed this note is removed sellable.notes = sellable.notes.replace( Sellable.NOTES_CREATED_VIA_SALE, "") product = sellable.product if sellable_created_via_sale else (Product( store=store, sellable=sellable)) product_data = data.get('product') product.manage_stock = product_data.get('manage_stock', False) # For clients that will control their inventory, we have to create a Storable if product.manage_stock and not store.get(Storable, product.id): storable = Storable(store=store, product=product) storable.maximum_quantity = 1000 return make_response( jsonify({ 'message': 'Product created', 'data': { 'id': sellable.id, 'barcode': sellable.barcode, 'description': sellable.description, 'status': sellable.status, } }), 201)
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;")
def testCreateEvent(self): store_list = [] p_data = _ProductEventData() ProductCreateEvent.connect(p_data.on_create) ProductEditEvent.connect(p_data.on_edit) ProductRemoveEvent.connect(p_data.on_delete) try: # Test product being created store = new_store() store_list.append(store) sellable = Sellable( store=store, description=u'Test 1234', price=Decimal(2), ) product = Product( store=store, sellable=sellable, ) store.commit() self.assertTrue(p_data.was_created) self.assertFalse(p_data.was_edited) self.assertFalse(p_data.was_deleted) self.assertEqual(p_data.product, product) p_data.reset() # Test product being edited and emmiting the event just once store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) sellable.notes = u'Notes' sellable.description = u'Test 666' product.weight = Decimal(10) store.commit() self.assertTrue(p_data.was_edited) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_deleted) self.assertEqual(p_data.product, product) self.assertEqual(p_data.emmit_count, 1) p_data.reset() # Test product being edited, editing Sellable store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) sellable.notes = u'Notes for test' store.commit() self.assertTrue(p_data.was_edited) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_deleted) self.assertEqual(p_data.product, product) self.assertEqual(p_data.emmit_count, 1) p_data.reset() # Test product being edited, editing Product itself store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) product.weight = Decimal(1) store.commit() self.assertTrue(p_data.was_edited) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_deleted) self.assertEqual(p_data.product, product) self.assertEqual(p_data.emmit_count, 1) p_data.reset() # Test product being edited, editing Product itself store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) product.weight = Decimal(1) store.commit() # self.assertTrue(p_data.was_edited) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_deleted) # self.assertEqual(p_data.product, product) # self.assertEqual(p_data.emmit_count, 1) p_data.reset() finally: # Test product being removed store = new_store() store_list.append(store) sellable = store.fetch(sellable) product = store.fetch(product) sellable.remove() store.commit() self.assertTrue(p_data.was_deleted) self.assertFalse(p_data.was_created) self.assertFalse(p_data.was_edited) self.assertEqual(p_data.product, product) self.assertEqual(p_data.emmit_count, 1) p_data.reset() for store in store_list: store.close()
class TestProduct(DomainTest): def setUp(self): DomainTest.setUp(self) sellable = self.create_sellable() self.product = Product(sellable=sellable, store=self.store) def test_get_main_supplier_info(self): self.failIf(self.product.get_main_supplier_info()) supplier = self.create_supplier() ProductSupplierInfo(store=self.store, supplier=supplier, product=self.product, is_main_supplier=True) self.failUnless(self.product.get_main_supplier_info()) def testGetComponents(self): self.assertEqual(list(self.product.get_components()), []) components = [] for i in range(3): component = self.create_product() product_component = ProductComponent(product=self.product, component=component, store=self.store) components.append(product_component) self.assertEqual(list(self.product.get_components()), components) def testHasComponents(self): self.assertFalse(self.product.has_components()) component = self.create_product() ProductComponent(product=self.product, component=component, store=self.store) self.assertTrue(self.product.has_components()) def testGetProductionCost(self): product = self.create_product() sellable = product.sellable sellable.cost = 50 production_cost = sellable.cost self.assertEqual(product.get_production_cost(), production_cost) def testIsComposedBy(self): component = self.create_product() self.assertEqual(self.product.is_composed_by(component), False) ProductComponent(product=self.product, component=component, store=self.store) self.assertEqual(self.product.is_composed_by(component), True) component2 = self.create_product() ProductComponent(product=component, component=component2, store=self.store) self.assertEqual(self.product.is_composed_by(component2), True) self.assertEqual(component.is_composed_by(component2), True) component3 = self.create_product() ProductComponent(product=self.product, component=component3, store=self.store) self.assertEqual(self.product.is_composed_by(component3), True) self.assertEqual(component.is_composed_by(component3), False) self.assertEqual(component2.is_composed_by(component3), False) def testSuppliers(self): product = self.create_product() supplier = self.create_supplier() info = ProductSupplierInfo(store=self.store, product=product, supplier=supplier) suppliers = list(product.get_suppliers_info()) # self.create_product already adds a supplier. so here we must have 2 self.assertEqual(len(suppliers), 2) self.assertEqual(info in suppliers, True) # product.suppliers should behave just like get_suppliers_info() self.assertEqual(len(list(product.suppliers)), 2) self.assertEqual(info in product.suppliers, True) self.assertEqual(product.is_supplied_by(supplier), True) def testCanRemove(self): product = self.create_product() storable = Storable(product=product, store=self.store) self.assertTrue(product.can_remove()) storable.increase_stock(1, get_current_branch(self.store), 0, 0) self.assertFalse(product.can_remove()) # Product was sold. sale = self.create_sale() sale.add_sellable(product.sellable, quantity=1, price=10) method = PaymentMethod.get_by_name(self.store, u'money') method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, sale.get_sale_subtotal()) sale.order() sale.confirm() self.assertFalse(product.can_remove()) # Product is a component. from stoqlib.domain.product import ProductComponent product = self.create_product(10) component = self.create_product(5) Storable(product=component, store=self.store) self.assertTrue(component.can_remove()) ProductComponent(product=product, component=component, store=self.store) self.assertFalse(component.can_remove()) # Product is used in a production. from stoqlib.domain.production import ProductionItem product = self.create_product() Storable(product=product, store=self.store) self.assertTrue(product.can_remove()) order = self.create_production_order() ProductionItem(product=product, order=order, quantity=1, store=self.store) self.assertFalse(product.can_remove()) def testRemove(self): product = self.create_product() Storable(product=product, store=self.store) total = self.store.find(Product, id=product.id).count() self.assertEquals(total, 1) product.remove() total = self.store.find(Product, id=product.id).count() self.assertEquals(total, 0) def testIncreaseDecreaseStock(self): branch = get_current_branch(self.store) product = self.create_product() storable = Storable(product=product, store=self.store) stock_item = storable.get_stock_item(branch, None) self.failIf(stock_item is not None) storable.increase_stock(1, branch, 0, 0) stock_item = storable.get_stock_item(branch, None) self.assertEquals(stock_item.stock_cost, 0) storable.increase_stock(1, branch, 0, 0, unit_cost=10) stock_item = storable.get_stock_item(branch, None) self.assertEquals(stock_item.stock_cost, 5) stock_item = storable.decrease_stock(1, branch, 0, 0) self.assertEquals(stock_item.stock_cost, 5) storable.increase_stock(1, branch, 0, 0) stock_item = storable.get_stock_item(branch, None) self.assertEquals(stock_item.stock_cost, 5) storable.increase_stock(2, branch, 0, 0, unit_cost=15) stock_item = storable.get_stock_item(branch, None) self.assertEquals(stock_item.stock_cost, 10) def test_lead_time(self): product = self.create_product() Storable(product=product, store=self.store) branch = get_current_branch(self.store) supplier1 = self.create_supplier() ProductSupplierInfo(store=self.store, product=product, supplier=supplier1, lead_time=10) self.assertEqual(product.get_max_lead_time(1, branch), 10) supplier2 = self.create_supplier() ProductSupplierInfo(store=self.store, product=product, supplier=supplier2, lead_time=20) self.assertEqual(product.get_max_lead_time(1, branch), 20) # Now for composed products product = self.create_product(create_supplier=False) product.is_composed = True product.production_time = 5 Storable(product=product, store=self.store) component = self.create_product(create_supplier=False) Storable(product=component, store=self.store) ProductSupplierInfo(store=self.store, product=component, supplier=supplier1, lead_time=7) self.assertEqual(component.get_max_lead_time(1, branch), 7) pc = ProductComponent(product=product, component=component, quantity=1, store=self.store) self.assertEqual(product.get_max_lead_time(1, branch), 12) # Increase the component stock component.storable.increase_stock(1, branch, 0, 0) self.assertEqual(product.get_max_lead_time(1, branch), 5) # Increase the quantity required: pc.quantity = 2 self.assertEqual(product.get_max_lead_time(1, branch), 12)