示例#1
0
    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)
示例#2
0
    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)
示例#3
0
    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
示例#4
0
 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
示例#5
0
    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())
示例#6
0
    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)
示例#7
0
    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())
示例#8
0
 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
示例#9
0
    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
示例#10
0
 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
示例#11
0
    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)
示例#12
0
    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
示例#13
0
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;")
示例#14
0
    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
示例#15
0
 def setUp(self):
     DomainTest.setUp(self)
     sellable = self.create_sellable()
     self.product = Product(sellable=sellable,
                            store=self.store)
示例#16
0
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)
示例#17
0
    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()
示例#18
0
    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
示例#19
0
    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)
示例#20
0
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;")
示例#21
0
    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()
示例#22
0
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)
示例#23
0
 def setUp(self):
     DomainTest.setUp(self)
     sellable = self.create_sellable()
     self.product = Product(sellable=sellable, store=self.store)