FabLabKasse.shopping.backend package¶
Subpackages¶
Submodules¶
FabLabKasse.shopping.backend.abstract module¶
abstract implementations of shopping and clients
base class and interface definition for specific implementations (see other files in this folder)
-
class
FabLabKasse.shopping.backend.abstract.
AbstractClient
(client_id=None, name='')[source]¶ Bases:
object
a client that can pay by pin
-
class
FabLabKasse.shopping.backend.abstract.
AbstractShoppingBackend
(cfg)[source]¶ Bases:
object
manages products, categories and orders (cart)
-
add_order_line
(prod_id, qty, comment=None)[source]¶ add product to cart
if not all values are allowed,
qty
is rounded up to the next possible amount.The user should only be asked for a comment by the GUI if
self.product_requires_text_entry(prod_id) == True
Parameters: - prod_id (int) – product
- qty (Decimal) – amount of product
- comment ((basestring, None)) – textual comment from the user, or None.
Raise: ProductNotFound
-
get_category_path
(current_category)[source]¶ return the category path from the root to the current category, excluding the root category
[child_of_root, …, parent_of_current, current_category]
Return type: list(Category)
-
get_current_order
()[source]¶ get selected order (or return 0 if switching between multiple orders is not supported)
-
get_current_total
()[source]¶ Returns: total sum of current order Return type: Decimal Note: The internal rounding must be consistent, which is needed by :class: FabLabKasse.shopping.payment_methods. That means that x,xx5 € must always be rounded up or always down. “Fair rounding” like Decimal.ROUND_HALF_EVEN is not allowed.
For example:
- add article costing 1,015 € -> get_current_total == x
- add article costing 0,990 € -> get_current_total == x + 0,99
This would not be true with the fair strategy “round second digit to even value if the third one is exactly 5” (1,02€ and 2,00€).
-
get_products
(current_category)[source]¶ return products in current category
Return type: list(Product)
-
get_subcategories
(current_category)[source]¶ return list(Category) of subclasses of the given category-id.
-
pay_order
(method)[source]¶ store payment of current order to database :param method: payment method object, whose type is used to determine where the order should be stored in the database method.amount_paid - method.amount_returned is how much money was gained by this sale, must be equal to self.get_current_total()
-
pay_order_on_client
(client)[source]¶ charge the order on client’s account
Parameters: client – AbstractClient Raises: DebtLimitExceeded when the client’s debt limit would be exceeded
-
print_receipt
(order_id)[source]¶ print the receipt for a given, already paid order_id
The receipt data must be stored in the backend, because for accountability reasons all receipt texts need to be stored anyway.
-
product_requires_text_entry
(prod_id)[source]¶ when adding prod_id, should the user be asked for a text entry for entering comments like his name?
-
static
round_money
(value)[source]¶ rounds money in Decimal representation to 2 places
Main purpose is shopping.backend.abstract.AbstractShoppingBackend.get_current_total(), since round() does behave weird. But maybe there are other applications too.
Parameters: value (float | Decimal) – an amount of money to be rounded Returns: money, rounded to 2 digits Return type: Decimal >>> AbstractShoppingBackend.round_money(Decimal('0.005')) Decimal('0.01') >>> AbstractShoppingBackend.round_money(Decimal('0.004')) Decimal('0.00')
-
search_from_text
(searchstr)[source]¶ search searchstr in products and categories :return: tuple (list of categories, products for table)
Return type: list(Product)
-
search_product_from_code
(code)[source]¶ search via barcode, PLU or similar unique-ID entry. code may be any string
Returns: product id Raises: ProductNotFound() if nothing found
-
-
class
FabLabKasse.shopping.backend.abstract.
AbstractShoppingBackendTest
(methodName='runTest')[source]¶ Bases:
unittest.case.TestCase
test the AbstractShoppingBackend class
TODO extend this test
-
class
FabLabKasse.shopping.backend.abstract.
Category
(categ_id, name, parent_id=None)[source]¶ Bases:
object
represents a category of Products
-
exception
FabLabKasse.shopping.backend.abstract.
DebtLimitExceeded
[source]¶ Bases:
exceptions.Exception
exception raised by pay_order_on_client: order not paid because the debt limit would have been exceeded
-
class
FabLabKasse.shopping.backend.abstract.
OrderLine
(order_line_id, qty, unit, name, price_per_unit, price_subtotal, delete_if_zero_qty=True)[source]¶ Bases:
object
one order line (roughly equal to a product in a shopping cart, although there may be multiple entries for one product)
Parameters: - id – id of order-line, must be unique and non-changing inside one Order() (if None: autogenerate id)
- qty (Decimal) – amount (“unlimited” number of digits is okay)
- unit (unicode) – product unit of sale
- name (unicode) – product name
- price_per_unit (Decimal) – price for one unit
- price_subtotal (Decimal) – price for
qty
*unit
of this product - delete_if_zero_qty (boolean) –
if the qty is zero and the user starts adding something else, then remove this line
[ usually True, set to False for products that also may as comment limes costing nothing ]
-
exception
FabLabKasse.shopping.backend.abstract.
PrinterError
[source]¶ Bases:
exceptions.Exception
cannot print receipt
-
class
FabLabKasse.shopping.backend.abstract.
Product
(prod_id, name, price, unit, location, categ_id=None, qty_rounding=0, text_entry_required=False)[source]¶ Bases:
object
simple representation for a product
Parameters: - prod_id (int) – numeric unique product ID
- categ_id (int | None) –
category ID of product, or None if the product is not directly visible
TODO hide these products from search, or a more explicit solution
- name (unicode) – Name of product
- location (unicode) – Location of product (shown to the user)
- unit (unicode) – Unit of sale for this product (e.g. piece, kilogram)
- price (Decimal) – price for one unit of this product
- qty_rounding (int | Decimal) –
Product can only be bought in multiples of this quantity, user (GUI) input will be rounded/truncated to the next multiple of this.
Set to 0 so that the product can be bought in arbitrarily small quantities.
example: you cannot buy half a t-shirt, so you set qty_rounding = 1
handling this is responsibility of the shopping backend
-
exception
FabLabKasse.shopping.backend.abstract.
ProductNotFound
[source]¶ Bases:
exceptions.Exception
requested product not found
-
FabLabKasse.shopping.backend.abstract.
float_to_decimal
(number, digits)[source]¶ convert float to decimal with rounding and strict error tolerances
If the given number cannot be represented as decimal with an error within 1/1000 of the last digit,
ValueError
is raised.Parameters: - number (float | Decimal) – a float that is nearly equal to a decimal number
- digits (int) – number of decimal places of the resulting value (max. 9)
Raise: ValueError
>>> float_to_decimal(1.424, 3) Decimal('1.424') >>> float_to_decimal(0.7, 1) Decimal('0.7')
-
FabLabKasse.shopping.backend.abstract.
format_money
(amount)[source]¶ format float as money string
You should best use Decimal as input. TODO: make moneysign interchangeable
Parameters: amount (float|Decimal) – amount of money Returns: amount formatted as string with Euro-Sign Return type: unicode >>> format_money(1.23) u'1,23 \u20ac' >>> format_money(3.741) u'3,741 \u20ac' >>> format_money(42.4242) u'42,424 \u20ac' >>> format_money(5.8899) u'5,89 \u20ac' >>> format_money(Decimal('1.23')) u'1,23 \u20ac' >>> format_money(Decimal('3.741')) u'3,741 \u20ac' >>> format_money(Decimal('42.4242')) u'42,424 \u20ac' >>> format_money(Decimal('5.8899')) u'5,89 \u20ac'
FabLabKasse.shopping.backend.dummy module¶
FabLabKasse.shopping.backend.legacy_offline_kassenbuch module¶
FabLabKasse.shopping.backend.oerp module¶
-
class
FabLabKasse.shopping.backend.oerp.
Client
(client_id=None, name='')[source]¶ Bases:
FabLabKasse.shopping.backend.abstract.AbstractClient
oerp implementation of AbstractClient. do not instantiate this yourself, but please rather use Client.from_oerp or ShoppingBackend.list_clients
-
class
FabLabKasse.shopping.backend.oerp.
ShoppingBackend
(cfg)[source]¶ Bases:
FabLabKasse.shopping.backend.abstract.AbstractShoppingBackend
OpenERP implementation of AbstractShoppingBackend
-
add_order_line
(prod_id, qty, comment=None)[source]¶ add product to cart
if not all values are allowed,
qty
is rounded up to the next possible amount.The user should only be asked for a comment by the GUI if
self.product_requires_text_entry(prod_id) == True
Parameters: - prod_id (int) – product
- qty (Decimal) – amount of product
- comment ((basestring, None)) – textual comment from the user, or None.
Raise: ProductNotFound
-
get_category_path
(current_category)[source]¶ return the category path from the root to the current category, excluding the root category
[child_of_root, …, parent_of_current, current_category]
Return type: list(Category)
-
get_current_order
()[source]¶ get selected order (or return 0 if switching between multiple orders is not supported)
-
get_current_total
()[source]¶ Returns: total sum of current order Return type: Decimal Note: The internal rounding must be consistent, which is needed by :class: FabLabKasse.shopping.payment_methods. That means that x,xx5 € must always be rounded up or always down. “Fair rounding” like Decimal.ROUND_HALF_EVEN is not allowed.
For example:
- add article costing 1,015 € -> get_current_total == x
- add article costing 0,990 € -> get_current_total == x + 0,99
This would not be true with the fair strategy “round second digit to even value if the third one is exactly 5” (1,02€ and 2,00€).
-
get_products
(current_category)[source]¶ return products in current category
Return type: list(Product)
-
get_subcategories
(current_category)[source]¶ return list(Category) of subclasses of the given category-id.
-
pay_order
(method)[source]¶ store payment of current order to database :param method: payment method object, whose type is used to determine where the order should be stored in the database method.amount_paid - method.amount_returned is how much money was gained by this sale, must be equal to self.get_current_total()
-
search_from_text
(searchstr)[source]¶ search searchstr in products and categories :return: tuple (list of categories, products for table)
Return type: list(Product)
-
search_product_from_code
(code)[source]¶ search via barcode, PLU or similar unique-ID entry. code may be any string
Returns: product id Raises: ProductNotFound() if nothing found
-