feat: allow basic support for item discounts
This commit is contained in:
parent
f8f745c243
commit
bdf9e46be5
|
@ -30,6 +30,7 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|||
|
||||
intersphinx_mapping = {
|
||||
'pyramid': ('https://docs.pylonsproject.org/projects/pyramid/en/latest/', None),
|
||||
'python': ('https://docs.python.org/3/', None),
|
||||
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
|
||||
'wuttaweb': ('https://rattailproject.org/docs/wuttaweb/', None),
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Sideshow -- Case/Special Order Tracker
|
||||
# Copyright © 2024 Lance Edgar
|
||||
# Copyright © 2024-2025 Lance Edgar
|
||||
#
|
||||
# This file is part of Sideshow.
|
||||
#
|
||||
|
@ -80,6 +80,32 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
return self.config.get_bool('sideshow.orders.allow_unknown_products',
|
||||
default=True)
|
||||
|
||||
def allow_item_discounts(self):
|
||||
"""
|
||||
Returns boolean indicating whether per-item discounts are
|
||||
allowed when creating an order.
|
||||
"""
|
||||
return self.config.get_bool('sideshow.orders.allow_item_discounts',
|
||||
default=False)
|
||||
|
||||
def allow_item_discounts_if_on_sale(self):
|
||||
"""
|
||||
Returns boolean indicating whether per-item discounts are
|
||||
allowed even when the item is already on sale.
|
||||
"""
|
||||
return self.config.get_bool('sideshow.orders.allow_item_discounts_if_on_sale',
|
||||
default=False)
|
||||
|
||||
def get_default_item_discount(self):
|
||||
"""
|
||||
Returns the default item discount percentage, e.g. 15.
|
||||
|
||||
:rtype: :class:`~python:decimal.Decimal` or ``None``
|
||||
"""
|
||||
discount = self.config.get('sideshow.orders.default_item_discount')
|
||||
if discount:
|
||||
return decimal.Decimal(discount)
|
||||
|
||||
def autocomplete_customers_external(self, session, term, user=None):
|
||||
"""
|
||||
Return autocomplete search results for :term:`external
|
||||
|
@ -430,7 +456,8 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
'vendor_item_code': product.vendor_item_code,
|
||||
}
|
||||
|
||||
def add_item(self, batch, product_info, order_qty, order_uom, user=None):
|
||||
def add_item(self, batch, product_info, order_qty, order_uom,
|
||||
discount_percent=None, user=None):
|
||||
"""
|
||||
Add a new item/row to the batch, for given product and quantity.
|
||||
|
||||
|
@ -451,6 +478,10 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
:attr:`~sideshow.db.model.batch.neworder.NewOrderBatchRow.order_uom`
|
||||
value for the new row.
|
||||
|
||||
:param discount_percent: Sets the
|
||||
:attr:`~sideshow.db.model.batch.neworder.NewOrderBatchRow.discount_percent`
|
||||
for the row, if allowed.
|
||||
|
||||
:param user:
|
||||
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
|
||||
is performing the action. This is used to set
|
||||
|
@ -518,12 +549,17 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
row.order_qty = order_qty
|
||||
row.order_uom = order_uom
|
||||
|
||||
# discount
|
||||
if self.allow_item_discounts():
|
||||
row.discount_percent = discount_percent or 0
|
||||
|
||||
# add row to batch
|
||||
self.add_row(batch, row)
|
||||
session.flush()
|
||||
return row
|
||||
|
||||
def update_item(self, row, product_info, order_qty, order_uom, user=None):
|
||||
def update_item(self, row, product_info, order_qty, order_uom,
|
||||
discount_percent=None, user=None):
|
||||
"""
|
||||
Update an item/row, per given product and quantity.
|
||||
|
||||
|
@ -544,6 +580,10 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
:attr:`~sideshow.db.model.batch.neworder.NewOrderBatchRow.order_uom`
|
||||
value for the row.
|
||||
|
||||
:param discount_percent: Sets the
|
||||
:attr:`~sideshow.db.model.batch.neworder.NewOrderBatchRow.discount_percent`
|
||||
for the row, if allowed.
|
||||
|
||||
:param user:
|
||||
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
|
||||
is performing the action. This is used to set
|
||||
|
@ -608,6 +648,10 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
row.order_qty = order_qty
|
||||
row.order_uom = order_uom
|
||||
|
||||
# discount
|
||||
if self.allow_item_discounts():
|
||||
row.discount_percent = discount_percent or 0
|
||||
|
||||
# nb. this may convert float to decimal etc.
|
||||
session.flush()
|
||||
session.refresh(row)
|
||||
|
@ -675,12 +719,19 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
# update row total price
|
||||
row.total_price = None
|
||||
if row.order_uom == enum.ORDER_UOM_CASE:
|
||||
# TODO: why are we not using case price again?
|
||||
# if row.case_price_quoted:
|
||||
# row.total_price = row.case_price_quoted * row.order_qty
|
||||
if row.unit_price_quoted is not None and row.case_size is not None:
|
||||
row.total_price = row.unit_price_quoted * row.case_size * row.order_qty
|
||||
else: # ORDER_UOM_UNIT (or similar)
|
||||
if row.unit_price_quoted is not None:
|
||||
row.total_price = row.unit_price_quoted * row.order_qty
|
||||
if row.total_price is not None:
|
||||
if row.discount_percent and self.allow_item_discounts():
|
||||
row.total_price = (float(row.total_price)
|
||||
* (100 - float(row.discount_percent))
|
||||
/ 100.0)
|
||||
row.total_price = decimal.Decimal(f'{row.total_price:0.2f}')
|
||||
|
||||
# update batch if total price changed
|
||||
|
@ -971,7 +1022,7 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
'unit_price_reg',
|
||||
'unit_price_sale',
|
||||
'sale_ends',
|
||||
# 'discount_percent',
|
||||
'discount_percent',
|
||||
'total_price',
|
||||
'special_order',
|
||||
]
|
||||
|
|
|
@ -19,6 +19,36 @@
|
|||
<h3 class="block is-size-3">Products</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="sideshow.orders.allow_item_discounts"
|
||||
v-model="simpleSettings['sideshow.orders.allow_item_discounts']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Allow per-item discounts
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field v-show="simpleSettings['sideshow.orders.allow_item_discounts']">
|
||||
<b-checkbox name="sideshow.orders.allow_item_discounts_if_on_sale"
|
||||
v-model="simpleSettings['sideshow.orders.allow_item_discounts_if_on_sale']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Allow discount even if item is on sale
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<div v-show="simpleSettings['sideshow.orders.allow_item_discounts']"
|
||||
class="level-left block">
|
||||
<div class="level-item">Default item discount</div>
|
||||
<div class="level-item">
|
||||
<b-input name="sideshow.orders.default_item_discount"
|
||||
v-model="simpleSettings['sideshow.orders.default_item_discount']"
|
||||
@input="settingsNeedSaved = true"
|
||||
style="width: 5rem;" />
|
||||
</div>
|
||||
<div class="level-item">%</div>
|
||||
</div>
|
||||
|
||||
<b-field label="Product Source">
|
||||
<b-select name="sideshow.orders.use_local_products"
|
||||
v-model="simpleSettings['sideshow.orders.use_local_products']"
|
||||
|
|
|
@ -663,11 +663,11 @@
|
|||
<b-field label="Discount" horizontal>
|
||||
<div class="level">
|
||||
<div class="level-item">
|
||||
<numeric-input v-model="productDiscountPercent"
|
||||
## TODO: needs numeric-input component
|
||||
<b-input v-model="productDiscountPercent"
|
||||
@input="refreshTotalPrice += 1"
|
||||
style="width: 5rem;"
|
||||
:disabled="!allowItemDiscount">
|
||||
</numeric-input>
|
||||
:disabled="!allowItemDiscount" />
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<span> %</span>
|
||||
|
@ -749,6 +749,13 @@
|
|||
</span>
|
||||
</${b}-table-column>
|
||||
|
||||
% if allow_item_discounts:
|
||||
<${b}-table-column label="Discount"
|
||||
v-slot="props">
|
||||
{{ props.row.discount_percent }}{{ props.row.discount_percent ? " %" : "" }}
|
||||
</${b}-table-column>
|
||||
% endif
|
||||
|
||||
<${b}-table-column label="Total"
|
||||
v-slot="props">
|
||||
<span :class="props.row.pricing_reflects_sale ? 'has-background-warning' : null">
|
||||
|
@ -890,6 +897,11 @@
|
|||
productUOM: defaultUOM,
|
||||
productCaseSize: null,
|
||||
|
||||
% if allow_item_discounts:
|
||||
productDiscountPercent: ${json.dumps(default_item_discount)|n},
|
||||
allowDiscountsIfOnSale: ${json.dumps(allow_item_discounts_if_on_sale)|n},
|
||||
% endif
|
||||
|
||||
pendingProduct: {},
|
||||
pendingProductRequiredFields: ${json.dumps(pending_product_required_fields)|n},
|
||||
## TODO
|
||||
|
@ -1011,6 +1023,19 @@
|
|||
return text
|
||||
},
|
||||
|
||||
% if allow_item_discounts:
|
||||
|
||||
allowItemDiscount() {
|
||||
if (!this.allowDiscountsIfOnSale) {
|
||||
if (this.productSalePriceDisplay) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
% endif
|
||||
|
||||
pendingProductGrossMargin() {
|
||||
let cost = this.pendingProduct.unit_cost
|
||||
let price = this.pendingProduct.unit_price_reg
|
||||
|
@ -1324,6 +1349,10 @@
|
|||
this.productSalePriceDisplay = null
|
||||
this.productSaleEndsDisplay = null
|
||||
this.productUnitChoices = this.defaultUnitChoices
|
||||
|
||||
% if allow_item_discounts:
|
||||
this.productDiscountPercent = ${json.dumps(default_item_discount)|n}
|
||||
% endif
|
||||
},
|
||||
|
||||
productChanged(productID) {
|
||||
|
@ -1360,6 +1389,10 @@
|
|||
this.productSalePriceDisplay = data.unit_price_sale_display
|
||||
this.productSaleEndsDisplay = data.sale_ends_display
|
||||
|
||||
% if allow_item_discounts:
|
||||
this.productDiscountPercent = this.allowItemDiscount ? data.default_item_discount : null
|
||||
% endif
|
||||
|
||||
// this.setProductUnitChoices(data.uom_choices)
|
||||
|
||||
% if request.use_oruga:
|
||||
|
@ -1434,6 +1467,10 @@
|
|||
this.productUnitChoices = this.defaultUnitChoices
|
||||
this.productUOM = this.defaultUOM
|
||||
|
||||
% if allow_item_discounts:
|
||||
this.productDiscountPercent = ${json.dumps(default_item_discount)|n}
|
||||
% endif
|
||||
|
||||
% if request.use_oruga:
|
||||
this.itemDialogTab = 'product'
|
||||
% else:
|
||||
|
@ -1488,6 +1525,10 @@
|
|||
this.productUnitChoices = row.order_uom_choices
|
||||
this.productUOM = row.order_uom
|
||||
|
||||
% if allow_item_discounts:
|
||||
this.productDiscountPercent = row.discount_percent
|
||||
% endif
|
||||
|
||||
// nb. hack to force refresh for vue3
|
||||
this.refreshProductDescription += 1
|
||||
this.refreshTotalPrice += 1
|
||||
|
@ -1538,6 +1579,10 @@
|
|||
params.product_info = this.pendingProduct
|
||||
}
|
||||
|
||||
% if allow_item_discounts:
|
||||
params.discount_percent = this.productDiscountPercent
|
||||
% endif
|
||||
|
||||
if (this.editItemRow) {
|
||||
params.action = 'update_item'
|
||||
params.uuid = this.editItemRow.uuid
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Sideshow -- Case/Special Order Tracker
|
||||
# Copyright © 2024 Lance Edgar
|
||||
# Copyright © 2024-2025 Lance Edgar
|
||||
#
|
||||
# This file is part of Sideshow.
|
||||
#
|
||||
|
@ -121,6 +121,7 @@ class NewOrderBatchView(BatchMasterView):
|
|||
'case_price_quoted',
|
||||
'order_qty',
|
||||
'order_uom',
|
||||
'discount_percent',
|
||||
'total_price',
|
||||
'status_code',
|
||||
]
|
||||
|
@ -167,6 +168,10 @@ class NewOrderBatchView(BatchMasterView):
|
|||
g.set_label('case_price_quoted', "Case Price", column_only=True)
|
||||
g.set_renderer('case_price_quoted', 'currency')
|
||||
|
||||
# discount_percent
|
||||
g.set_renderer('discount_percent', 'percent')
|
||||
g.set_label('discount_percent', "Disc. %", column_only=True)
|
||||
|
||||
# total_price
|
||||
g.set_renderer('total_price', 'currency')
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Sideshow -- Case/Special Order Tracker
|
||||
# Copyright © 2024 Lance Edgar
|
||||
# Copyright © 2024-2025 Lance Edgar
|
||||
#
|
||||
# This file is part of Sideshow.
|
||||
#
|
||||
|
@ -135,6 +135,7 @@ class OrderView(MasterView):
|
|||
'special_order',
|
||||
'order_qty',
|
||||
'order_uom',
|
||||
'discount_percent',
|
||||
'total_price',
|
||||
'status_code',
|
||||
]
|
||||
|
@ -284,10 +285,19 @@ class OrderView(MasterView):
|
|||
for row in batch.rows],
|
||||
'default_uom_choices': self.get_default_uom_choices(),
|
||||
'default_uom': None, # TODO?
|
||||
'allow_item_discounts': self.batch_handler.allow_item_discounts(),
|
||||
'allow_unknown_products': (self.batch_handler.allow_unknown_products()
|
||||
and self.has_perm('create_unknown_product')),
|
||||
'pending_product_required_fields': self.get_pending_product_required_fields(),
|
||||
})
|
||||
|
||||
if context['allow_item_discounts']:
|
||||
context['allow_item_discounts_if_on_sale'] = self.batch_handler\
|
||||
.allow_item_discounts_if_on_sale()
|
||||
# nb. render quantity so that '10.0' => '10'
|
||||
context['default_item_discount'] = self.app.render_quantity(
|
||||
self.batch_handler.get_default_item_discount())
|
||||
|
||||
return self.render_to_response('create', context)
|
||||
|
||||
def get_current_batch(self):
|
||||
|
@ -564,11 +574,15 @@ class OrderView(MasterView):
|
|||
if 'case_price_quoted' in data and 'case_price_quoted_display' not in data:
|
||||
data['case_price_quoted_display'] = self.app.render_currency(data['case_price_quoted'])
|
||||
|
||||
if 'default_item_discount' not in data:
|
||||
data['default_item_discount'] = self.batch_handler.get_default_item_discount()
|
||||
|
||||
decimal_fields = [
|
||||
'case_size',
|
||||
'unit_price_reg',
|
||||
'unit_price_quoted',
|
||||
'case_price_quoted',
|
||||
'default_item_discount',
|
||||
]
|
||||
|
||||
for field in decimal_fields:
|
||||
|
@ -589,8 +603,11 @@ class OrderView(MasterView):
|
|||
* :meth:`update_item()`
|
||||
* :meth:`delete_item()`
|
||||
"""
|
||||
kw = {'user': self.request.user}
|
||||
if 'discount_percent' in data and self.batch_handler.allow_item_discounts():
|
||||
kw['discount_percent'] = data['discount_percent']
|
||||
row = self.batch_handler.add_item(batch, data['product_info'],
|
||||
data['order_qty'], data['order_uom'])
|
||||
data['order_qty'], data['order_uom'], **kw)
|
||||
|
||||
return {'batch': self.normalize_batch(batch),
|
||||
'row': self.normalize_row(row)}
|
||||
|
@ -619,8 +636,11 @@ class OrderView(MasterView):
|
|||
if row.batch is not batch:
|
||||
return {'error': "Row is for wrong batch"}
|
||||
|
||||
kw = {'user': self.request.user}
|
||||
if 'discount_percent' in data and self.batch_handler.allow_item_discounts():
|
||||
kw['discount_percent'] = data['discount_percent']
|
||||
self.batch_handler.update_item(row, data['product_info'],
|
||||
data['order_qty'], data['order_uom'])
|
||||
data['order_qty'], data['order_uom'], **kw)
|
||||
|
||||
return {'batch': self.normalize_batch(batch),
|
||||
'row': self.normalize_row(row)}
|
||||
|
@ -715,6 +735,7 @@ class OrderView(MasterView):
|
|||
'order_qty': float(row.order_qty),
|
||||
'order_uom': row.order_uom,
|
||||
'order_uom_choices': self.get_default_uom_choices(),
|
||||
'discount_percent': self.app.render_quantity(row.discount_percent),
|
||||
'unit_price_quoted': float(row.unit_price_quoted) if row.unit_price_quoted is not None else None,
|
||||
'unit_price_quoted_display': self.app.render_currency(row.unit_price_quoted),
|
||||
'case_price_quoted': float(row.case_price_quoted) if row.case_price_quoted is not None else None,
|
||||
|
@ -857,6 +878,10 @@ class OrderView(MasterView):
|
|||
# order_uom
|
||||
#g.set_renderer('order_uom', self.grid_render_enum, enum=enum.OrderUOM)
|
||||
|
||||
# discount_percent
|
||||
g.set_renderer('discount_percent', 'percent')
|
||||
g.set_label('discount_percent', "Disc. %", column_only=True)
|
||||
|
||||
# total_price
|
||||
g.set_renderer('total_price', g.render_currency)
|
||||
|
||||
|
@ -895,6 +920,12 @@ class OrderView(MasterView):
|
|||
'default': 'true'},
|
||||
|
||||
# products
|
||||
{'name': 'sideshow.orders.allow_item_discounts',
|
||||
'type': bool},
|
||||
{'name': 'sideshow.orders.allow_item_discounts_if_on_sale',
|
||||
'type': bool},
|
||||
{'name': 'sideshow.orders.default_item_discount',
|
||||
'type': float},
|
||||
{'name': 'sideshow.orders.use_local_products',
|
||||
# nb. this is really a bool but we present as string in config UI
|
||||
#'type': bool,
|
||||
|
|
|
@ -20,36 +20,66 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
def make_handler(self):
|
||||
return mod.NewOrderBatchHandler(self.config)
|
||||
|
||||
def tets_use_local_customers(self):
|
||||
def test_use_local_customers(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# true by default
|
||||
self.assertTrue(handler.use_local_customers())
|
||||
|
||||
# config can disable
|
||||
config.setdefault('sideshow.orders.use_local_customers', 'false')
|
||||
self.config.setdefault('sideshow.orders.use_local_customers', 'false')
|
||||
self.assertFalse(handler.use_local_customers())
|
||||
|
||||
def tets_use_local_products(self):
|
||||
def test_use_local_products(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# true by default
|
||||
self.assertTrue(handler.use_local_products())
|
||||
|
||||
# config can disable
|
||||
config.setdefault('sideshow.orders.use_local_products', 'false')
|
||||
self.config.setdefault('sideshow.orders.use_local_products', 'false')
|
||||
self.assertFalse(handler.use_local_products())
|
||||
|
||||
def tets_allow_unknown_products(self):
|
||||
def test_allow_unknown_products(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# true by default
|
||||
self.assertTrue(handler.allow_unknown_products())
|
||||
|
||||
# config can disable
|
||||
config.setdefault('sideshow.orders.allow_unknown_products', 'false')
|
||||
self.config.setdefault('sideshow.orders.allow_unknown_products', 'false')
|
||||
self.assertFalse(handler.allow_unknown_products())
|
||||
|
||||
def test_allow_item_discounts(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# false by default
|
||||
self.assertFalse(handler.allow_item_discounts())
|
||||
|
||||
# config can enable
|
||||
self.config.setdefault('sideshow.orders.allow_item_discounts', 'true')
|
||||
self.assertTrue(handler.allow_item_discounts())
|
||||
|
||||
def test_allow_item_discounts_if_on_sale(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# false by default
|
||||
self.assertFalse(handler.allow_item_discounts_if_on_sale())
|
||||
|
||||
# config can enable
|
||||
self.config.setdefault('sideshow.orders.allow_item_discounts_if_on_sale', 'true')
|
||||
self.assertTrue(handler.allow_item_discounts_if_on_sale())
|
||||
|
||||
def test_get_default_item_discount(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# null by default
|
||||
self.assertIsNone(handler.get_default_item_discount())
|
||||
|
||||
# config can define
|
||||
self.config.setdefault('sideshow.orders.default_item_discount', '15')
|
||||
self.assertEqual(handler.get_default_item_discount(), decimal.Decimal('15.00'))
|
||||
|
||||
def test_autocomplete_customers_external(self):
|
||||
handler = self.make_handler()
|
||||
self.assertRaises(NotImplementedError, handler.autocomplete_customers_external,
|
||||
|
@ -327,7 +357,7 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
self.config.setdefault('sideshow.orders.allow_unknown_products', 'false')
|
||||
self.assertRaises(TypeError, handler.add_item, batch, kw, 1, enum.ORDER_UOM_UNIT)
|
||||
|
||||
# local product
|
||||
# local product w/ discount
|
||||
local = model.LocalProduct(scancode='07430500002',
|
||||
description='Vinegar',
|
||||
size='2oz',
|
||||
|
@ -335,7 +365,9 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
case_size=12)
|
||||
self.session.add(local)
|
||||
self.session.flush()
|
||||
row = handler.add_item(batch, local.uuid.hex, 1, enum.ORDER_UOM_CASE)
|
||||
with patch.object(handler, 'allow_item_discounts', return_value=True):
|
||||
row = handler.add_item(batch, local.uuid.hex, 1, enum.ORDER_UOM_CASE,
|
||||
discount_percent=15)
|
||||
self.session.flush()
|
||||
self.session.refresh(row)
|
||||
self.session.refresh(local)
|
||||
|
@ -359,7 +391,8 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
self.assertEqual(row.unit_price_reg, decimal.Decimal('2.99'))
|
||||
self.assertEqual(row.unit_price_quoted, decimal.Decimal('2.99'))
|
||||
self.assertEqual(row.case_price_quoted, decimal.Decimal('35.88'))
|
||||
self.assertEqual(row.total_price, decimal.Decimal('35.88'))
|
||||
self.assertEqual(row.discount_percent, decimal.Decimal('15.00'))
|
||||
self.assertEqual(row.total_price, decimal.Decimal('30.50'))
|
||||
|
||||
# local product, not found
|
||||
mock_uuid = self.app.make_true_uuid()
|
||||
|
@ -511,8 +544,10 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
self.config.setdefault('sideshow.orders.allow_unknown_products', 'false')
|
||||
self.assertRaises(TypeError, handler.update_item, row, kw, 1, enum.ORDER_UOM_UNIT)
|
||||
|
||||
# update w/ local product
|
||||
handler.update_item(row, local.uuid.hex, 1, enum.ORDER_UOM_CASE)
|
||||
# update w/ local product and discount percent
|
||||
with patch.object(handler, 'allow_item_discounts', return_value=True):
|
||||
handler.update_item(row, local.uuid.hex, 1, enum.ORDER_UOM_CASE,
|
||||
discount_percent=15)
|
||||
self.assertIsNone(row.product_id)
|
||||
# nb. pending remains intact here
|
||||
self.assertIsNotNone(row.pending_product)
|
||||
|
@ -536,7 +571,8 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
self.assertEqual(row.case_price_quoted, decimal.Decimal('47.88'))
|
||||
self.assertEqual(row.order_qty, 1)
|
||||
self.assertEqual(row.order_uom, enum.ORDER_UOM_CASE)
|
||||
self.assertEqual(row.total_price, decimal.Decimal('47.88'))
|
||||
self.assertEqual(row.discount_percent, decimal.Decimal('15.00'))
|
||||
self.assertEqual(row.total_price, decimal.Decimal('40.70'))
|
||||
|
||||
# update w/ local, not found
|
||||
mock_uuid = self.app.make_true_uuid()
|
||||
|
|
|
@ -52,6 +52,7 @@ class TestOrderView(WebTestCase):
|
|||
self.pyramid_config.include('sideshow.web.views')
|
||||
self.config.setdefault('wutta.batch.neworder.handler.spec',
|
||||
'sideshow.batch.neworder:NewOrderBatchHandler')
|
||||
self.config.setdefault('sideshow.orders.allow_item_discounts', 'true')
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
@ -577,6 +578,7 @@ class TestOrderView(WebTestCase):
|
|||
def test_add_item(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
self.config.setdefault('sideshow.orders.allow_item_discounts', 'true')
|
||||
handler = NewOrderBatchHandler(self.config)
|
||||
view = self.make_view()
|
||||
|
||||
|
@ -594,6 +596,7 @@ class TestOrderView(WebTestCase):
|
|||
},
|
||||
'order_qty': 1,
|
||||
'order_uom': enum.ORDER_UOM_UNIT,
|
||||
'discount_percent': 10,
|
||||
}
|
||||
|
||||
with patch.object(view, 'batch_handler', create=True, new=handler):
|
||||
|
@ -620,6 +623,7 @@ class TestOrderView(WebTestCase):
|
|||
def test_update_item(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
self.config.setdefault('sideshow.orders.allow_item_discounts', 'true')
|
||||
handler = NewOrderBatchHandler(self.config)
|
||||
view = self.make_view()
|
||||
|
||||
|
@ -638,6 +642,7 @@ class TestOrderView(WebTestCase):
|
|||
},
|
||||
'order_qty': 1,
|
||||
'order_uom': enum.ORDER_UOM_CASE,
|
||||
'discount_percent': 15,
|
||||
}
|
||||
|
||||
with patch.object(view, 'batch_handler', create=True, new=handler):
|
||||
|
|
Loading…
Reference in a new issue