Example #1
0
    def test_get_available_sellables_query(self):
        # Sellable and query without supplier
        sellable = self.create_sellable()
        self.create_storable(product=sellable.product,
                             branch=self.create_branch())

        self.assertIn(
            sellable,
            self.store.find(Sellable,
                            Sellable.get_available_sellables_query(self.store)))

        sellable.close()
        self.assertNotIn(
            sellable,
            self.store.find(Sellable,
                            Sellable.get_available_sellables_query(self.store)))

        delivery_sellable = sysparam(self.store).DELIVERY_SERVICE.sellable
        delivery_sellable.status = Sellable.STATUS_AVAILABLE
        # Deliveries are treated differently, that's why they should
        # not be present here
        self.assertNotIn(
            sellable,
            self.store.find(Sellable,
                            Sellable.get_available_sellables_query(self.store)))
Example #2
0
    def test_with_unblocked_sellables_query(self):
        # This is used in the purchase wizard and breaks storm
        from stoqlib.domain.sellable import Sellable

        p1 = self.create_product()
        supplier = self.create_supplier()

        # Product should appear when querying without a supplier
        query = Sellable.get_unblocked_sellables_query(self.store)
        results = self.store.find(ProductFullStockView, query)
        self.assertTrue(p1.id in [p.product_id for p in results])

        # But should not appear when querying with a supplier
        # When querying using the supplier, we should use the
        # ProductFullStockSupplierView instead.
        query = Sellable.get_unblocked_sellables_query(self.store, supplier=supplier)
        results = self.store.find(ProductFullStockItemSupplierView, query)
        self.assertFalse(p1.id in [p.id for p in results])

        # Now relate the two
        ProductSupplierInfo(store=self.store, supplier=supplier, product=p1, is_main_supplier=True)

        # And it should appear now
        query = Sellable.get_unblocked_sellables_query(self.store, supplier=supplier)
        results = self.store.find(ProductFullStockItemSupplierView, query)
        self.assertTrue(p1.id in [s.product_id for s in results])

        # But should not appear for a different supplier
        other_supplier = self.create_supplier()
        query = Sellable.get_unblocked_sellables_query(self.store, supplier=other_supplier)
        results = self.store.find(ProductFullStockItemSupplierView, query)
        self.assertFalse(p1.id in [s.product_id for s in results])
Example #3
0
 def create_model(self, store):
     tax_constant = SellableTaxConstant.get_by_type(TaxType.SERVICE, self.store)
     sellable = Sellable(description=u"", price=currency(0), store=store)
     sellable.tax_constant = tax_constant
     sellable.unit_id = sysparam.get_object_id("SUGGESTED_UNIT")
     model = Service(sellable=sellable, store=store)
     return model
Example #4
0
 def _update_default_sellable_code(self, category=None):
     if category:
         query = (Sellable.category_id == category.id)
         code = Sellable.get_max_value(self.store, Sellable.code, query=query)
     else:
         code = Sellable.get_max_value(self.store, Sellable.code)
     self.code.update(next_value_for(code))
Example #5
0
    def test_get_unblocked_by_category(self):
        s1 = self.create_sellable()
        s2 = self.create_sellable()
        s3 = self.create_sellable()

        c1 = self.create_sellable_category()
        c2 = self.create_sellable_category()
        s1.category = c1
        s2.category = c2

        self.assertEqual(
            set([s1, s2, s3]),
            set(Sellable.get_unblocked_by_categories(
                self.store, [c1, c2], include_uncategorized=True)))

        self.assertEqual(
            set([s1, s3]),
            set(Sellable.get_unblocked_by_categories(
                self.store, [c1], include_uncategorized=True)))

        self.assertEqual(
            set([s1, s3]),
            set(Sellable.get_unblocked_by_categories(
                self.store, [c1], include_uncategorized=True)))

        self.assertEqual(
            set([s1]),
            set(Sellable.get_unblocked_by_categories(
                self.store, [c1], include_uncategorized=False)))

        self.assertEqual(
            set([s3]),
            set(Sellable.get_unblocked_by_categories(
                self.store, [], include_uncategorized=True)))
Example #6
0
    def test_get_unblocked_sellables(self):
        # Sellable and query without supplier
        sellable = self.create_sellable()
        available = Sellable.get_unblocked_sellables(self.store)
        self.assertTrue(sellable in list(available))

        # Sellable without supplier, but querying with one
        supplier = self.create_supplier()
        available = Sellable.get_unblocked_sellables(self.store,
                                                     supplier=supplier)
        self.assertFalse(sellable in list(available))

        # Relate the two
        from stoqlib.domain.product import ProductSupplierInfo
        ProductSupplierInfo(store=self.store,
                            supplier=supplier,
                            product=sellable.product,
                            is_main_supplier=True)

        # Now the sellable should appear in the results
        available = Sellable.get_unblocked_sellables(self.store,
                                                     supplier=supplier)
        self.assertTrue(sellable in list(available))

        # Now the sellable should appear in the results
        storable = Storable(product=sellable.product, store=self.store)
        available = Sellable.get_unblocked_sellables(self.store,
                                                     storable=storable)
        self.assertTrue(sellable in list(available))
Example #7
0
    def create_service(self, description=u"Description", price=10):
        from stoqlib.domain.sellable import Sellable, SellableTaxConstant
        from stoqlib.domain.service import Service

        tax_constant = SellableTaxConstant.get_by_type(TaxType.SERVICE, self.store)
        sellable = Sellable(price=price, description=description, store=self.store)
        sellable.tax_constant = tax_constant
        service = Service(sellable=sellable, store=self.store)
        return service
Example #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)
     Storable(product=model, store=store)
     return model
Example #9
0
 def create_model(self, store):
     tax_constant = SellableTaxConstant.get_by_type(TaxType.SERVICE, self.store)
     sellable = Sellable(description=u'',
                         price=currency(0),
                         store=store)
     sellable.status = Sellable.STATUS_AVAILABLE
     sellable.tax_constant = tax_constant
     sellable.unit = sysparam(self.store).SUGGESTED_UNIT
     model = Service(sellable=sellable, store=store)
     return model
Example #10
0
    def _set_delivery_default(self, store):
        if self.has_object("DELIVERY_SERVICE"):
            return
        from stoqlib.domain.sellable import Sellable, SellableTaxConstant
        from stoqlib.domain.service import Service

        tax_constant = SellableTaxConstant.get_by_type(TaxType.SERVICE, store)
        sellable = Sellable(store=store, description=_(u"Delivery"))
        sellable.tax_constant = tax_constant
        service = Service(sellable=sellable, store=store)
        self.set_object(store, "DELIVERY_SERVICE", service)
Example #11
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)
     # 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
Example #12
0
 def test_category_name(self):
     sellable = Sellable(category=None,
                         cost=50,
                         description=u"Test",
                         price=currency(100),
                         store=self.store)
     sellable.max_discount = 0
     cat = self.create_client_category(u'Cat 1')
     cat_price = ClientCategoryPrice(sellable=sellable, category=cat,
                                     price=150, max_discount=0,
                                     store=self.store)
     self.assertEqual(cat_price.category_name, u'Cat 1')
Example #13
0
    def process_one(self, data, fields, store):
        tax = store.fetch(self.tax_constant)
        sellable = Sellable(store=store,
                            description=data.description,
                            price=int(data.price),
                            cost=int(data.cost))
        sellable.tax_constant = tax
        sellable.code = data.barcode
        sellable.barcode = data.barcode

        Service(sellable=sellable,
                store=store)
Example #14
0
    def test_markup(self):
        sellable = Sellable(category=None,
                            cost=0,
                            store=self.store)
        cat = self.create_client_category(u'Cat 1')
        cat_price = ClientCategoryPrice(sellable=sellable, category=cat,
                                        price=150, max_discount=0,
                                        store=self.store)
        self.assertEqual(cat_price.markup, 0)
        sellable.cost = 10
        self.assertEqual(cat_price.markup, 1400)

        cat_price.markup = 10
Example #15
0
 def create_delivery_service(self):
     from stoqlib.domain.sellable import (Sellable,
                                          SellableTaxConstant)
     from stoqlib.domain.service import Service
     key = u"DELIVERY_SERVICE"
     store = new_store()
     tax_constant = SellableTaxConstant.get_by_type(TaxType.SERVICE, store)
     sellable = Sellable(description=_(u'Delivery'),
                         store=store)
     sellable.tax_constant = tax_constant
     service = Service(sellable=sellable, store=store)
     self._set_schema(key, service.id)
     store.commit(close=True)
Example #16
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(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)
Example #17
0
    def test_price_on_sale_price_getter(self):
        sellable = Sellable(category=self._category,
                            cost=50,
                            description=u"Test",
                            price=100,
                            store=self.store)

        self.assertEquals(sellable.price, 100)
        sellable.on_sale_price = 80
        self.assertEquals(sellable.price, 80)

        # - Old promotion
        sellable.on_sale_start_date = localdate(2001, 1, 1)
        sellable.on_sale_end_date = localdate(2002, 1, 1)
        self.assertEquals(sellable.price, 100)

        # - Future promotion
        sellable.on_sale_start_date = localdate(3001, 1, 1)
        sellable.on_sale_end_date = localdate(3002, 1, 1)
        self.assertEquals(sellable.price, 100)

        # Current promotion
        sellable.on_sale_start_date = localdate(2001, 1, 1)
        sellable.on_sale_end_date = localdate(3002, 1, 1)
        self.assertEquals(sellable.price, 80)
Example #18
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
Example #19
0
 def get_sellable_view_query(self):
     branch = api.get_current_branch(self.store)
     branch_query = self.sellable_view.branch_id == branch.id
     sellable_query = Sellable.get_unblocked_sellables_query(self.store,
                                                             storable=True)
     query = And(branch_query, sellable_query)
     return self.sellable_view, query
Example #20
0
 def get_sellable_view_query(self):
     """This method should return a tuple containing the viewable that should
     be used and a query that should filter the sellables that can and cannot
     be added to this step.
     """
     return (self.sellable_view,
             Sellable.get_unblocked_sellables_query(self.store))
Example #21
0
 def get_sellable_view_query(self):
     branch = api.get_current_branch(self.store)
     branch_query = Or(ProductStockItem.branch_id == branch.id,
                       Eq(ProductStockItem.branch_id, None))
     query = And(branch_query,
                 Sellable.get_available_sellables_query(self.store))
     return self.sellable_view, query
Example #22
0
 def get_sellable_view_query(self):
     branch = api.get_current_branch(self.store)
     branch_query = Or(Field('_stock_summary', 'branch_id') == branch.id,
                       Eq(Field('_stock_summary', 'branch_id'), None))
     query = And(branch_query,
                 Sellable.get_available_sellables_query(self.store))
     return self.sellable_view, query
Example #23
0
 def get_sellable_view_query(self):
     branch = api.get_current_branch(self.store)
     branch_query = Or(ProductStockItem.branch_id == branch.id,
                       Eq(ProductStockItem.branch_id, None))
     query = And(branch_query,
                 Sellable.get_available_sellables_query(self.store))
     return self.sellable_view, query
Example #24
0
 def get_sellable_view_query(self):
     branch = api.get_current_branch(self.store)
     branch_query = Or(Field('_stock_summary', 'branch_id') == branch.id,
                       Eq(Field('_stock_summary', 'branch_id'), None))
     query = And(branch_query,
                 Sellable.get_available_sellables_query(self.store))
     return self.sellable_view, query
 def get_sellable_view_query(self):
     # The stock quantity of consigned products can not be
     # decreased manually. See bug 5212.
     query = And(Eq(Product.consignment, False),
                 self.sellable_view.branch_id == self.model.branch_id,
                 Sellable.get_available_sellables_query(self.store))
     return self.sellable_view, query
Example #26
0
 def get_sellable_view_query(self):
     # The stock quantity of consigned products can not be
     # decreased manually. See bug 5212.
     query = And(Eq(Product.consignment, False),
                 self.sellable_view.branch_id == self.model.branch_id,
                 Sellable.get_available_sellables_query(self.store))
     return self.sellable_view, query
Example #27
0
 def get_sellable_view_query(self):
     """This method should return a tuple containing the viewable that should
     be used and a query that should filter the sellables that can and cannot
     be added to this step.
     """
     return (self.sellable_view,
             Sellable.get_unblocked_sellables_query(self.store))
Example #28
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
Example #29
0
 def get_sellable_view_query(self):
     return (
         self.sellable_view,
         # FIXME: How to do this using sellable_view.find_by_branch ?
         And(
             Or(ProductStockItem.branch_id == self.model.branch.id,
                Eq(ProductStockItem.branch_id, None)),
             Sellable.get_available_sellables_query(self.store)))
Example #30
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
Example #31
0
    def test_can_remove(self):
        sellable = Sellable(store=self.store)
        self.assertTrue(sellable.can_remove())

        sellable = self.create_sellable()
        storable = Storable(product=sellable.product, store=self.store)
        self.assertTrue(sellable.can_remove())

        branch = get_current_branch(self.store)
        storable.increase_stock(1, branch,
                                StockTransactionHistory.TYPE_INITIAL, None)
        sale = self.create_sale()
        sale.status = Sale.STATUS_QUOTE
        sale.branch = branch
        sale.add_sellable(sellable)
        self.assertFalse(sellable.can_remove())

        # Can't remove the sellable if it's in a purchase.
        from stoqlib.domain.purchase import PurchaseItem
        sellable = self.create_sellable()
        Storable(product=sellable.product, store=self.store)
        self.assertTrue(sellable.can_remove())
        PurchaseItem(store=self.store,
                     quantity=8, quantity_received=0,
                     cost=125, base_cost=125,
                     sellable=sellable,
                     order=self.create_purchase_order())
        self.assertFalse(sellable.can_remove())

        # The delivery service cannot be removed.
        sellable = sysparam.get_object(self.store, 'DELIVERY_SERVICE').sellable
        self.assertFalse(sellable.can_remove())
Example #32
0
 def test_commission(self):
     self._category.salesperson_commission = 10
     sellable = Sellable(description=u"TX342",
                         category=self._category,
                         store=self.store)
     self.assertTrue(
         sellable.commission == self._category.salesperson_commission,
         (u"Expected salesperson commission: %r, got %r" %
          (self._category.salesperson_commission, sellable.commission)))
Example #33
0
 def test_price_based_on_category_markup(self):
     # When the price isn't defined, but the category and the cost. In this
     # case the sellable must have the price calculated applying the category's
     # markup in the sellable's cost.
     self._category.suggested_markup = 0
     sellable = Sellable(description=u"MX123",
                         commission=0,
                         cost=100,
                         category=self._category,
                         store=self.store)
     sellable.max_discount = 0
     self.assertTrue(sellable.markup == self._category.get_markup(),
                     (u"Expected markup: %r, got %r" %
                      (self._category.get_markup(), sellable.markup)))
     price = sellable.cost * (sellable.markup / currency(100) + 1)
     self.assertTrue(sellable.price == price,
                     (u"Expected price: %r, got %r" %
                      (price, sellable.price)))
Example #34
0
 def get_sellable_view_query(self):
     branch = self.model.branch
     branch_query = ProductStockItem.branch_id == branch.id
     # The stock quantity of consigned products can not be
     # decreased manually. See bug 5212.
     query = And(branch_query,
                 Product.consignment == False,
                 Sellable.get_available_sellables_query(self.store))
     return self.sellable_view, query
Example #35
0
    def test_price_based_on_specified_markup(self):
        # When the price isn't defined, but the category, markup and the cost.
        # In this case the category's markup must be ignored and the price
        # calculated applying the markup specified in the sellable's cost.
        sellable = Sellable(description=u"FY123",
                            category=self._category,
                            cost=100,
                            store=self.store)
        sellable.markup = 5
        self.assertEquals(sellable.markup, 5)
        self.assertEquals(sellable.price, 105)

        sellable.cost = Decimal('100.33')
        sellable.markup = 7
        self.assertEquals(sellable.price, currency('107.35'))

        sellable.markup = 8
        self.assertEquals(sellable.price, currency('108.36'))
Example #36
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
Example #37
0
    def get_sellable_view_query(self):
        branch = self.model.branch
        # Also include products that are not storable
        branch_query = Or(self.sellable_view.branch_id == branch.id, Eq(self.sellable_view.branch_id, None))

        # The stock quantity of consigned products can not be
        # decreased manually. See bug 5212.
        query = And(branch_query, Sellable.get_available_sellables_query(self.store))
        return self.sellable_view, query
Example #38
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)
Example #39
0
 def create_sellable(self, price=None, product=True,
                     description=u'Description', code=u''):
     from stoqlib.domain.product import Product
     from stoqlib.domain.service import Service
     from stoqlib.domain.sellable import Sellable
     tax_constant_id = sysparam.get_object_id('DEFAULT_PRODUCT_TAX_CONSTANT')
     if price is None:
         price = 10
     sellable = Sellable(cost=125,
                         price=price,
                         description=description,
                         store=self.store)
     sellable.code = code
     sellable.tax_constant_id = tax_constant_id
     if product:
         Product(sellable=sellable, store=self.store)
     else:
         Service(sellable=sellable, store=self.store)
     return sellable
Example #40
0
 def get_sellable_view_query(self):
     return (
         self.sellable_view,
         # FIXME: How to do this using sellable_view.find_by_branch ?
         And(
             Or(
                 Field('_stock_summary',
                       'branch_id') == self.model.branch.id,
                 Eq(Field('_stock_summary', 'branch_id'), None)),
             Sellable.get_available_sellables_query(self.store)))
Example #41
0
 def test_price_based_on_category_markup(self):
     # When the price isn't defined, but the category and the cost. In this
     # case the sellable must have the price calculated applying the category's
     # markup in the sellable's cost.
     self._category.suggested_markup = 0
     sellable = Sellable(description=u"MX123",
                         commission=0,
                         cost=100,
                         category=self._category,
                         store=self.store)
     sellable.max_discount = 0
     self.failUnless(sellable.markup == self._category.get_markup(),
                     (u"Expected markup: %r, got %r"
                      % (self._category.get_markup(),
                         sellable.markup)))
     price = sellable.cost * (sellable.markup / currency(100) + 1)
     self.failUnless(sellable.price == price,
                     (u"Expected price: %r, got %r"
                      % (price, sellable.price)))
Example #42
0
    def get_sellable_view_query(self):
        branch = self.model.branch
        # Also include products that are not storable
        branch_query = Or(self.sellable_view.branch_id == branch.id,
                          Eq(self.sellable_view.branch_id, None))

        # The stock quantity of consigned products can not be
        # decreased manually. See bug 5212.
        query = And(branch_query,
                    Sellable.get_available_sellables_query(self.store))
        return self.sellable_view, query
    def test_run_wizard(self, run_dialog, new_store):
        run_dialog.return_value = None
        new_store.return_value = self.store
        query = Sellable.get_unblocked_sellables_query(self.store)
        dialog = PurchaseSellableSearch(store=self.store,
                                        search_spec=ProductFullStockView,
                                        search_query=query)

        with mock.patch.object(self.store, 'commit'):
            with mock.patch.object(self.store, 'close'):
                self.click(dialog._toolbar.new_button)
                run_dialog.assert_called_once_with(ProductCreateWizard, dialog,
                                                   self.store)
Example #44
0
    def test_get_unblocked_by_category_query(self):
        s1 = self.create_sellable()
        s2 = self.create_sellable()
        s3 = self.create_sellable()

        c1 = self.create_sellable_category()
        c2 = self.create_sellable_category()
        s1.category = c1
        s2.category = c2

        query = Sellable.get_unblocked_by_categories_query(
            self.store, [c1, c2], include_uncategorized=True)
        self.assertEqual(
            set([s1, s2, s3]),
            set(self.store.find(Sellable, query)))

        query = Sellable.get_unblocked_by_categories_query(
            self.store, [c1], include_uncategorized=True)
        self.assertEqual(
            set([s1, s3]),
            set(self.store.find(Sellable, query)))

        query = Sellable.get_unblocked_by_categories_query(
            self.store, [c1], include_uncategorized=True)
        self.assertEqual(
            set([s1, s3]),
            set(self.store.find(Sellable, query)))

        query = Sellable.get_unblocked_by_categories_query(
            self.store, [c1], include_uncategorized=False)
        self.assertEqual(
            set([s1]),
            set(self.store.find(Sellable, query)))

        query = Sellable.get_unblocked_by_categories_query(
            self.store, [], include_uncategorized=True)
        self.assertEqual(
            set([s3]),
            set(self.store.find(Sellable, query)))
Example #45
0
    def test_with_unblocked_sellables_query(self):
        # This is used in the purchase wizard and breaks storm
        from stoqlib.domain.product import ProductSupplierInfo
        from stoqlib.domain.sellable import Sellable

        p1 = self.create_product()
        supplier = self.create_supplier()

        # Product should appear when querying without a supplier
        query = Sellable.get_unblocked_sellables_query(self.store)
        results = self.store.find(ProductFullStockView, query)
        self.assertTrue(p1.id in [p.product_id for p in results])

        # But should not appear when querying with a supplier
        # When querying using the supplier, we should use the
        # ProductFullStockSupplierView instead.
        query = Sellable.get_unblocked_sellables_query(self.store,
                                                       supplier=supplier)
        results = self.store.find(ProductFullStockItemSupplierView, query)
        self.assertFalse(p1.id in [p.id for p in results])

        # Now relate the two
        ProductSupplierInfo(store=self.store,
                            supplier=supplier,
                            product=p1,
                            is_main_supplier=True)

        # And it should appear now
        query = Sellable.get_unblocked_sellables_query(self.store,
                                                       supplier=supplier)
        results = self.store.find(ProductFullStockItemSupplierView, query)
        self.assertTrue(p1.id in [s.product_id for s in results])

        # But should not appear for a different supplier
        other_supplier = self.create_supplier()
        query = Sellable.get_unblocked_sellables_query(self.store,
                                                       supplier=other_supplier)
        results = self.store.find(ProductFullStockItemSupplierView, query)
        self.assertFalse(p1.id in [s.product_id for s in results])
Example #46
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
Example #47
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
Example #48
0
    def test_get_unblocked_sellables(self):
        # Sellable and query without supplier
        sellable = self.create_sellable()
        available = Sellable.get_unblocked_sellables(self.store)
        self.assertTrue(sellable in list(available))

        # Sellable without supplier, but querying with one
        supplier = self.create_supplier()
        available = Sellable.get_unblocked_sellables(self.store,
                                                     supplier=supplier)
        self.assertFalse(sellable in list(available))

        # Relate the two
        from stoqlib.domain.product import ProductSupplierInfo
        ProductSupplierInfo(store=self.store,
                            supplier=supplier,
                            product=sellable.product,
                            is_main_supplier=True)

        # Now the sellable should appear in the results
        available = Sellable.get_unblocked_sellables(self.store,
                                                     supplier=supplier)
        self.assertTrue(sellable in list(available))
Example #49
0
    def get_sellable_view_query(self):
        supplier = self.model.supplier
        if self.wizard.all_products:
            supplier = None

        # If we our query includes the supplier, we must use another viewable,
        # that actually joins with that table
        if supplier:
            viewable = ProductFullStockItemSupplierView
        else:
            viewable = self.sellable_view

        query = Sellable.get_unblocked_sellables_query(self.store, supplier=supplier,
                                                       consigned=self.model.consigned)
        return viewable, query
Example #50
0
    def test_run_editor(self, run_dialog, new_store):
        run_dialog.return_value = None
        new_store.return_value = self.store
        query = Sellable.get_unblocked_sellables_query(self.store)
        dialog = AdvancedSellableSearch(store=self.store,
                                        table=ProductFullStockView,
                                        query=query)
        dialog.search.refresh()
        dialog.results.select(dialog.results[0])
        product = dialog.results[0].product

        with mock.patch.object(self.store, 'commit'):
            with mock.patch.object(self.store, 'close'):
                self.click(dialog._toolbar.edit_button)
                run_dialog.assert_called_once_with(ProductEditor, dialog, self.store, product, visual_mode=False)
Example #51
0
    def _get_sellables_query(self):
        categories = [c.category for c in self.category_tree if
                      c.selected and c is not self._uncategorized_products]
        include_uncategorized = self._uncategorized_products.selected

        query = Sellable.get_unblocked_by_categories_query(
            self.store, categories, include_uncategorized)

        queries = [query]
        if self.model.product_manufacturer:
            queries.append(Product.manufacturer == self.model.product_manufacturer)
        if self.model.product_brand:
            queries.append(Product.brand == self.model.product_brand)
        if self.model.product_family:
            queries.append(Product.family == self.model.product_family)

        return And(*queries)
Example #52
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)
Example #53
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.set_icms_template(taxes['icms'])
        product.set_pis_template(taxes['pis'])
        product.set_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)
Example #54
0
    def _get_sellables(self):
        selected = [
            c.category for c in self.category_tree
            if c.selected and c is not self._uncategorized_products
        ]
        include_uncategorized = self._uncategorized_products.selected

        sellables = Sellable.get_unblocked_by_categories(
            self.store, selected, include_uncategorized)

        if self.model.product_manufacturer:
            sellables = sellables.find(
                Product.manufacturer == self.model.product_manufacturer)
        if self.model.product_brand:
            sellables = sellables.find(
                Product.brand == self.model.product_brand)
        if self.model.product_family:
            sellables = sellables.find(
                Product.family == self.model.product_family)

        return sellables
Example #55
0
    def test_price_based_on_specified_markup(self):
        # When the price isn't defined, but the category, markup and the cost.
        # In this case the category's markup must be ignored and the price
        # calculated applying the markup specified in the sellable's cost.
        sellable = Sellable(description=u"FY123",
                            category=self._category,
                            cost=100,
                            store=self.store)
        sellable.markup = 5
        self.assertEquals(sellable.markup, 5)
        self.assertEquals(sellable.price, 105)

        sellable.cost = Decimal('100.33')
        sellable.markup = 7
        self.assertEquals(sellable.price, currency('107.35'))

        sellable.markup = 8
        self.assertEquals(sellable.price, currency('108.36'))
Example #56
0
 def _update_default_sellable_code(self):
     code = Sellable.get_max_value(self.store, Sellable.code)
     self.code.update(next_value_for(code))
Example #57
0
 def get_sellable_view_query(self):
     query = Sellable.get_unblocked_sellables_query(self.store)
     return self.sellable_view, query
Example #58
0
 def get_sellable_view_query(self):
     sellable_query = And(
         Sellable.get_unblocked_sellables_query(self.store, storable=False),
         self.sellable_view.branch_id == self.model.source_branch_id)
     return self.sellable_view, sellable_query
Example #59
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)