diff --git a/brewman/models/__init__.py b/brewman/models/__init__.py index f79c2fe0..5e8306a9 100644 --- a/brewman/models/__init__.py +++ b/brewman/models/__init__.py @@ -8,14 +8,118 @@ from sqlalchemy.orm import sessionmaker DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), expire_on_commit=False)) Base = declarative_base() + def initialize_sql(engine): - # from brewman.models.messaging import Tag, Thread, Subscriber, thread_tag, Post - # from brewman.models.voucher import Attendance, Batch, Fingerprint, Inventory, Journal, Product, SalaryDeduction, Voucher, VoucherType - # from brewman.models.master import Product, AttendanceType, CostCenter, Employee, Ledger, LedgerBase, LedgerType, ProductGroup - # from brewman.models.auth import Client, Group, Role, User, role_group, user_group - # from .master import DbSetting DBSession.configure(bind=engine) Base.metadata.bind = engine - # Base.metadata.create_all(engine) + if not schema_exists(engine): + fixtures(engine) +def schema_exists(engine): + from brewman.models.master import DbSetting + with engine.connect() as connection: + return engine.dialect.has_table(connection, DbSetting.__tablename__) + + +def fixtures(engine): + import transaction + import uuid + from brewman.models.messaging import Tag, Thread, Subscriber, thread_tag, Post + from brewman.models.voucher import Attendance, Batch, Fingerprint, Inventory, Journal, Product, SalaryDeduction, Voucher, VoucherType + from brewman.models.master import Product, AttendanceType, CostCenter, Employee, Ledger, LedgerBase, LedgerType, ProductGroup + from brewman.models.auth import Client, Group, Role, User, role_group, user_group + + Base.metadata.create_all(engine) + + user = User('Admin', '123456', False, uuid.UUID('8de98592-76d9-c74d-bb3f-d6184d388b5a')) + DBSession.add(user) + + groups = [Group('Owner', uuid.UUID('52e08c0c-048a-784f-be10-6e129ad4b5d4')), + Group('Accountant', uuid.UUID('bc4c2d23-437a-984d-abd4-7d5fce677547')), + Group('Accounts Manager', uuid.UUID('cfc44fa7-3392-5b45-b311-5959333f568f'))] + + for group in groups: + DBSession.add(group) + user.groups.append(group) + + roles = [Role('Attendance', uuid.UUID('09d05434-a09a-fa45-963b-769a2e3fc667')), + Role('Trial Balance', uuid.UUID('3b099fec-ddc5-4243-b30e-afb78d9ca14a')), + Role('Cash Flow', uuid.UUID('c4d3ae29-420b-ea4c-ae90-00a356263fd9')), + Role('Cost Centers', uuid.UUID('6fcc1a20-6aec-e840-b334-1632b34aeab8')), + Role('Users', uuid.UUID('c5b7d9d7-f178-0e45-8ea4-bf4e08ec901b')), + Role('Daybook', uuid.UUID('c3edb554-a057-8942-8030-37b8e926d583')), + Role('Edit Posted Vouchers', uuid.UUID('d6675817-ddf5-bf40-9de6-fa223eb4aaa6')), + Role('Employees', uuid.UUID('e4edd0ac-7f5d-e64d-8611-73fdc4cd8ba2')), + Role('Fingerprints', uuid.UUID('d9c45323-f997-ba46-9407-8a7145f0828b')), + Role('Issue', uuid.UUID('03b602eb-f58a-b94f-af58-8cb47d7849d0')), + Role('Journal', uuid.UUID('7661388f-62ce-1c41-8e0d-0326ee5d4018')), + Role('Accounts', uuid.UUID('f438262f-72dd-2f4e-9186-5abc3af44fba')), + Role('Product Ledger', uuid.UUID('018a2408-e804-1446-90c5-b015829da6ba')), + Role('Backdated Vouchers', uuid.UUID('b67b2062-5ca7-134f-8258-5d284dd92426')), + Role('Payment', uuid.UUID('f85c0b52-c3fd-7141-8957-7a56cdc014a4')), + Role('Post Vouchers', uuid.UUID('36e741da-1a57-b047-a59e-dcd58fcf4338')), + Role('Products', uuid.UUID('74fa6d21-eebb-e14c-8153-bebc57190ab4')), + Role('Product Groups', uuid.UUID('08413a22-cf88-fd43-b2b7-365d2951d99f')), + Role('Profit & Loss', uuid.UUID('0492ebb3-76f3-204e-ab94-bbfe880f0691')), + Role('Purchase', uuid.UUID('12335acb-8630-2d41-a191-1517c8d172de')), + Role('Purchase Entries', uuid.UUID('78a6422b-aa11-174c-9dfa-412a99e87e02')), + Role('Purchase Return', uuid.UUID('ab33196e-d9e4-114c-ac8c-997954363756')), + Role('Receipt', uuid.UUID('1f1ce53e-76ff-a346-974a-65db6f606e5f')), + Role('Closing Stock', uuid.UUID('97515732-24e4-c94d-9585-d4bd7f6c7891')), + Role('Ledger', uuid.UUID('a2120944-243f-3f49-be57-0ad633ce4801')), + Role('Raw Material Cost', uuid.UUID('d462842b-baf1-2343-95e5-ffdba9bbc163')), + Role('Edit Other User\'s Vouchers', uuid.UUID('a8328891-7ce2-a943-8c29-2eabc1ffeea3')), + Role('Clients', uuid.UUID('cfad44f0-f2a9-7045-89d7-9019cf0f371a')), + Role('Salary Deduction', uuid.UUID('92d70e80-1c32-384d-959e-abf84b804696')), + Role('Messages', uuid.UUID('f586d128-b6d9-4090-a913-78fcbdb68e59')), + Role('Lock Date', uuid.UUID('d52de0be-9388-4b0b-a359-7e122ab6e53a')), + Role('Net Transactions', uuid.UUID('2c40f7cf-67fc-4efa-a670-8d16a2e7884d')), + Role('Balance Sheet', uuid.UUID('40deb018-b8f2-460a-88be-8972c9fcdf04')), + Role('Advanced Delete', uuid.UUID('197ebcd2-bc4a-4b65-a138-ce942ece32ea')), + Role('Rebase', uuid.UUID('de204a88-5f9d-4579-a2d6-aa2f25efde42')), + Role('Reset Stock', uuid.UUID('aecaf82f-aa41-4634-b754-0c1308b621b1')), + Role('Reconcile', uuid.UUID('a5cb51cb-e38e-4705-84a7-cc1e9a8b866b')), + Role('Stock Movement', uuid.UUID('20b707ee-2b59-41ad-be87-76d5fe1efca8')), + Role('Purchases', uuid.UUID('cf7019c8-3fd3-45b0-9a42-601029ce5b71')), + Role('Dashboard', uuid.UUID('53eecc09-bd06-4890-b6f5-6885dda762d4'))] + + for role in roles: + DBSession.add(role) + groups[0].roles.append(role) + + cost_centers = [CostCenter('Overall', uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), True), + CostCenter('Purchase', uuid.UUID('7b845f95-dfef-fa4a-897c-f0baf15284a3'), True), + CostCenter('Kitchen', uuid.UUID('b2d398ce-e3cc-c542-9feb-5d7783e899df'), True)] + + for cost_center in cost_centers: + DBSession.add(cost_center) + + ledgers = [Ledger(1, 'All Purchases', 2, True, False, uuid.UUID('7b845f95-dfef-fa4a-897c-f0baf15284a3'), + uuid.UUID('240dd899-c413-854c-a7eb-67a29d154490'), True), + Ledger(1, 'Local Purchase', 9, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), + uuid.UUID('d2b75912-505f-2548-9093-466dfff6a0f9'), True), + Ledger(1, 'ESI/PF - Payable', 11, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), + uuid.UUID('42277912-cc18-854b-b134-9f4b00dba419'), True), + Ledger(2, 'ESI/PF - Expense', 7, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), + uuid.UUID('d2a1a286-e900-764b-a1a5-9f4b00dbb940'), True), + Ledger(1, 'Cash in Hand', 1, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), + uuid.UUID('ed2341bb-80b8-9649-90db-f9aaca183bb3'), True), + Ledger(1, 'Staff Salary', 7, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), + uuid.UUID('5c2b54d0-c174-004d-a0d5-92cdaadcefa7'), True), + Ledger(1, 'Suspense', 4, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), + uuid.UUID('3854e317-6f3b-5142-ab26-9c44d4cddd08'), True)] + + for ledger in ledgers: + DBSession.add(ledger) + + product_group = ProductGroup('Suspense', uuid.UUID('ae59a20c-87bb-444a-8abb-915ad5e58b83'), True) + DBSession.add(product_group) + + product = Product(1, 'Suspense', '', 1, '', 1, False, uuid.UUID('ae59a20c-87bb-444a-8abb-915ad5e58b83'), + uuid.UUID('240dd899-c413-854c-a7eb-67a29d154490'), 0, True, + uuid.UUID('aa79a643-9ddc-4790-ac7f-a41f9efb4c15'), True) + DBSession.add(product) + + transaction.commit() + diff --git a/brewman/models/auth.py b/brewman/models/auth.py index 73f95f9a..96f76410 100644 --- a/brewman/models/auth.py +++ b/brewman/models/auth.py @@ -1,17 +1,17 @@ import random import string import uuid -from sqlalchemy.schema import ForeignKey, Table -from brewman.models.guidtype import GUID from hashlib import md5 +from sqlalchemy.schema import ForeignKey, Table from sqlalchemy import Column, Boolean, Unicode, Integer - from sqlalchemy.orm import synonym, relationship +from brewman.models.guidtype import GUID from brewman.models import Base from brewman.models import DBSession + def encrypt(val): return md5(val.encode('utf-8') + "Salt".encode('utf-8')).hexdigest() @@ -68,7 +68,6 @@ user_group = Table( Column('GroupID', GUID(), ForeignKey('auth_groups.GroupID')) ) - role_group = Table( 'auth_rolegroups', Base.metadata, Column('RoleGroupID', GUID(), primary_key=True, default=uuid.uuid4), @@ -101,10 +100,11 @@ class User(Base): def __name__(self): return self.name - def __init__(self, name=None, password=None, locked_out=None): + def __init__(self, name=None, password=None, locked_out=None, id=None): self.name = name self.password = password self.locked_out = locked_out + self.id = id @classmethod def by_name(cls, name): @@ -120,6 +120,8 @@ class User(Base): @classmethod def auth(cls, name, password): + if password is None: + return False, None user = cls.by_name(name) if not user: return False, None @@ -150,8 +152,10 @@ class Group(Base): id = Column('GroupID', GUID(), primary_key=True, default=uuid.uuid4) name = Column('Name', Unicode(255), unique=True) - def __init__(self, name=None): + + def __init__(self, name=None, id=None): self.name = name + self.id = id @classmethod def by_id(cls, id): @@ -170,8 +174,9 @@ class Role(Base): groups = relationship("Group", secondary=role_group, backref="roles") - def __init__(self, name=None): + def __init__(self, name=None, id=None): self.name = name + self.id = id @classmethod def list(cls): diff --git a/brewman/models/master.py b/brewman/models/master.py index fbb2be9a..d57ff6af 100644 --- a/brewman/models/master.py +++ b/brewman/models/master.py @@ -17,7 +17,7 @@ class Product(Base): units = Column('Units', Unicode(255), nullable=False) fraction = Column('Fraction', Numeric, nullable=False) fraction_units = Column('FractionUnits', Unicode(255), nullable=False) - yeild = Column('Yeild', Numeric, nullable=False) + product_yield = Column('ProductYield', Numeric, nullable=False) show_for_purchase = Column('ShowForPurchase', Boolean, nullable=False) product_group_id = Column('ProductGroupID', GUID(), ForeignKey('entities_productgroups.ProductGroupID'), nullable=False) @@ -31,20 +31,21 @@ class Product(Base): ledger = relationship('Ledger', primaryjoin="Ledger.id==Product.ledger_id", backref='products') - def __init__(self, code=None, name=None, units=None, fraction=None, fraction_units=None, yeild=None, + def __init__(self, code=None, name=None, units=None, fraction=None, fraction_units=None, product_yield=None, show_for_purchase=None, product_group_id=None, ledger_id=None, price=None, discontinued=None, - is_fixture=False): + id=None,is_fixture=False): self.code = code self.name = name self.units = units self.fraction = fraction self.fraction_units = fraction_units - self.yeild = yeild + self.product_yield = product_yield self.show_for_purchase = show_for_purchase self.product_group_id = product_group_id self.ledger_id = ledger_id self.price = price self.discontinued = discontinued + self.id = id self.is_fixture = is_fixture @property @@ -108,8 +109,9 @@ class ProductGroup(Base): products = relationship('Product', backref='product_group') - def __init__(self, name=None, is_fixture=False): + def __init__(self, name=None, id=None, is_fixture=False): self.name = name + self.id = id self.is_fixture = is_fixture @classmethod @@ -135,8 +137,9 @@ class CostCenter(Base): def __name__(self): return self.name - def __init__(self, name=None, is_fixture=False): + def __init__(self, name=None, id=None, is_fixture=False): self.name = name + self.id = id self.is_fixture = is_fixture @classmethod @@ -190,13 +193,14 @@ class LedgerBase(Base): return LedgerType.by_id(self.type) def __init__(self, code=None, name=None, type=None, is_active=None, is_reconcilable=False, costcenter_id=None, - is_fixture=False): + id=None, is_fixture=False): self.code = code self.name = name self.type = type self.is_active = is_active self.is_reconcilable = is_reconcilable self.costcenter_id = costcenter_id + self.id = id self.is_fixture = is_fixture @classmethod diff --git a/brewman/models/voucher.py b/brewman/models/voucher.py index e484d7cb..b559b704 100644 --- a/brewman/models/voucher.py +++ b/brewman/models/voucher.py @@ -54,7 +54,7 @@ class Voucher(Base): id = Column('VoucherID', GUID(), primary_key=True, default=uuid.uuid4) date = Column('Date', DateTime, nullable=False) reconcile_date = Column('ReconcileDate', DateTime, nullable=False) - is_reconciled = Column('IsReconciled', DateTime, nullable=False) + is_reconciled = Column('IsReconciled', Boolean, nullable=False) narration = Column('Narration', Unicode(1000), nullable=False) posted = Column('Posted', Boolean, nullable=False) creation_date = Column('CreationDate', DateTime(timezone=True), nullable=False) diff --git a/brewman/static/partial/product-detail.html b/brewman/static/partial/product-detail.html index 4160ca9f..6a9f0825 100644 --- a/brewman/static/partial/product-detail.html +++ b/brewman/static/partial/product-detail.html @@ -37,17 +37,17 @@
- +
- +
- +
diff --git a/brewman/views/__init__.py b/brewman/views/__init__.py index c37ba0d1..3631b150 100644 --- a/brewman/views/__init__.py +++ b/brewman/views/__init__.py @@ -1,3 +1,4 @@ +from decimal import Decimal from datetime import date, datetime, timedelta, time import uuid import re @@ -23,7 +24,7 @@ def forbidden(request): response.status_int = 401 return response else: - return HTTPFound(location=request.route_url('login')) + return HTTPFound(location=request.route_url('login', _query={'came_from': request.path})) @view_config(route_name='favicon') @@ -58,3 +59,20 @@ def get_lock_info(): def to_uuid(value): p = re.compile('^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$') return uuid.UUID(value) if p.match(value) else None + +def to_decimal(value, default=0): + import re + _parser = re.compile(r""" # A numeric string consists of: + (?P[-+])? # an optional sign, followed by either... + ( + (?=\d|\.\d) # ...a number (with at least one digit) + (?P\d*) # having a (possibly empty) integer part + (\.(?P\d*))? # followed by an optional fractional part + | + (?Ps)? # ...an (optionally signaling) + NaN # NaN + (?P\d*) # with (possibly empty) diagnostic info. + ) + \Z + """, re.VERBOSE | re.IGNORECASE).match + return Decimal(value) if _parser(value.strip()) is not None else default diff --git a/brewman/views/employee.py b/brewman/views/employee.py index e8403af0..d3c2e0c6 100644 --- a/brewman/views/employee.py +++ b/brewman/views/employee.py @@ -29,16 +29,45 @@ def html(request): @view_config(request_method='POST', route_name='api_employee', renderer='json', permission='Employees') @TryCatchFunction def save(request): - is_active = request.json_body['IsActive'] - joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y') - leaving_date = None if is_active else datetime.datetime.strptime(request.json_body['LeavingDate'], - '%d-%b-%Y') + name = request.json_body.get('Name', '').strip() + if name == '': + raise ValidationError('Name cannot be blank') - item = Employee(0, request.json_body['Name'], is_active, - uuid.UUID(request.json_body['CostCenter']['CostCenterID']), - request.json_body['Designation'], int(request.json_body['Salary']), - int(request.json_body['ServicePoints']), - joining_date, leaving_date).create() + cost_center_id = uuid.UUID(request.json_body['CostCenter']['CostCenterID']) + designation = request.json_body.get('Designation', '').strip() + + try: + salary = int(request.json_body['Salary']) + if salary < 0: + raise ValidationError("Salary must be an integer >= 0") + except (ValueError, KeyError): + raise ValidationError("Salary must be an integer >= 0") + + try: + service_points = int(request.json_body['ServicePoints']) + if service_points < 0: + raise ValidationError("Service Points must be an integer >= 0") + except (ValueError, KeyError): + raise ValidationError("Service Points must be an integer >= 0") + + try: + joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y') + except (ValueError, KeyError, TypeError): + raise ValidationError("Joining Date is not a valid date") + + is_active = request.json_body['IsActive'] + try: + if is_active: + leaving_date = None + else: + leaving_date = datetime.datetime.strptime(request.json_body['LeavingDate'], '%d-%b-%Y') + if leaving_date < joining_date: + raise ValidationError("Leaving Date cannot be less than Joining Date") + except (ValueError, KeyError, TypeError): + raise ValidationError("Leaving Date is not a valid date") + + item = Employee(0, name, is_active, cost_center_id, designation, salary, service_points, joining_date, + leaving_date).create() transaction.commit() return employee_info(item.id) @@ -49,15 +78,44 @@ def update(request): item = Employee.by_id(uuid.UUID(request.matchdict['id'])) if item.is_fixture: raise ValidationError("{0} is a fixture and cannot be edited or deleted.".format(item.name)) - item.name = request.json_body['Name'] + + item.name = request.json_body.get('Name', '').strip() + if item.name == '': + raise ValidationError('Name cannot be blank') + + item.cost_center_id = uuid.UUID(request.json_body['CostCenter']['CostCenterID']) + item.designation = request.json_body.get('Designation', '').strip() + + try: + item.salary = int(request.json_body['Salary']) + if item.salary < 0: + raise ValidationError("Salary must be an integer >= 0") + except (ValueError, KeyError): + raise ValidationError("Salary must be an integer >= 0") + + try: + item.service_points = int(request.json_body['ServicePoints']) + if item.service_points < 0: + raise ValidationError("Service Points must be an integer >= 0") + except (ValueError, KeyError): + raise ValidationError("Service Points must be an integer >= 0") + + try: + item.joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y') + except (ValueError, KeyError, TypeError): + raise ValidationError("Joining Date is not a valid date") + item.is_active = request.json_body['IsActive'] - item.costcenter_id = uuid.UUID(request.json_body['CostCenter']['CostCenterID']) - item.designation = request.json_body['Designation'] - item.salary = int(request.json_body['Salary']) - item.service_points = int(request.json_body['ServicePoints']) - item.joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y') - item.leaving_date = None if item.is_active else datetime.datetime.strptime(request.json_body['LeavingDate'], - '%d-%b-%Y') + try: + if item.is_active: + item.leaving_date = None + else: + item.leaving_date = datetime.datetime.strptime(request.json_body['LeavingDate'], '%d-%b-%Y') + if item.leaving_date < item.joining_date: + raise ValidationError("Leaving Date cannot be less than Joining Date") + except (ValueError, KeyError, TypeError): + raise ValidationError("Leaving Date is not a valid date") + transaction.commit() return employee_info(item.id) @@ -141,8 +199,8 @@ def employee_info(id): def delete_with_data(employee): suspense_ledger = Ledger.by_id(Ledger.suspense()) - query = Voucher.query().options(joinedload_all(Voucher.journals, Journal.ledger, innerjoin=True))\ - .filter(Voucher.journals.any(Journal.ledger_id == employee.id))\ + query = Voucher.query().options(joinedload_all(Voucher.journals, Journal.ledger, innerjoin=True)) \ + .filter(Voucher.journals.any(Journal.ledger_id == employee.id)) \ .all() for voucher in query: diff --git a/brewman/views/product.py b/brewman/views/product.py index a0987a3c..cafddaaf 100644 --- a/brewman/views/product.py +++ b/brewman/views/product.py @@ -1,4 +1,4 @@ -from decimal import Decimal +from decimal import Decimal, InvalidOperation import uuid from pyramid.response import Response from pyramid.security import authenticated_userid @@ -27,21 +27,42 @@ def html(request): @TryCatchFunction def save(request): json = request.json_body + name = json.get('Name', '').strip() if name == '': raise ValidationError('Name cannot be blank') + units = json.get('Units', '').strip() - fraction = Decimal(json.get('Fraction', 0)) + + try: + fraction = Decimal(json.get('Fraction', 0)) + if fraction <= 0 or fraction > 1: + raise ValidationError("Fraction must be a decimal > 0 and <= 1") + except (ValueError, InvalidOperation): + raise ValidationError("Fraction must be a decimal > 0 and <= 1") + fraction_units = json.get('FractionUnits', '').strip() - yeild = Decimal(json.get('Yeild', 1)) + + try: + product_yield = Decimal(json.get('ProductYield', 1)) + if product_yield < 0 or product_yield > 1: + raise ValidationError("Yield must be a decimal >= 0 <= 1") + except (ValueError, InvalidOperation): + raise ValidationError("Yield must be a decimal >= 0 <= 1") + show_for_purchase = json.get('ShowForPurchase', True) product_group = json.get('ProductGroup', None) if product_group is None: raise ValidationError('Please choose a product group') product_group_id = uuid.UUID(product_group['ProductGroupID']) - price = Decimal(json.get('Price', 0)) + try: + price = Decimal(json.get('Price', 0)) + if price < 0: + raise ValidationError("Price must be a decimal > 0") + except (ValueError, InvalidOperation): + raise ValidationError("Price must be a decimal > 0") discontinued = json.get('Discontinued', False) - item = Product(0, name, units, fraction, fraction_units, yeild, show_for_purchase, product_group_id, + item = Product(0, name, units, fraction, fraction_units, product_yield, show_for_purchase, product_group_id, Ledger.all_purchases(), price, discontinued).create() transaction.commit() return product_info(item.id) @@ -55,13 +76,30 @@ def update(request): raise ValidationError("{0} is a fixture and cannot be edited or deleted.".format(item.full_name)) item.name = request.json_body['Name'].strip() item.units = request.json_body['Units'].strip() - item.fraction = Decimal(request.json_body['Fraction']) + try: + item.fraction = Decimal(request.json_body['Fraction']) + if item.fraction <= 0 or item.fraction > 1: + raise ValidationError("Fraction must be a decimal > 0 and <= 1") + except (ValueError, InvalidOperation): + raise ValidationError("Fraction must be a decimal > 0 and <= 1") item.fraction_units = request.json_body['FractionUnits'] - item.yeild = Decimal(request.json_body['Yeild']) + + try: + item.product_yield = Decimal(request.json_body['ProductYield']) + if item.product_yield < 0 or item.product_yield > 1: + raise ValidationError("Yield must be a decimal >= 0 <= 1") + except (ValueError, InvalidOperation): + raise ValidationError("Yield must be a decimal >= 0 <= 1") + item.show_for_purchase = request.json_body['ShowForPurchase'] item.product_group_id = uuid.UUID(request.json_body['ProductGroup']['ProductGroupID']) item.ledger_id = Ledger.all_purchases() - item.price = Decimal(request.json_body['Price']) + try: + item.price = Decimal(request.json_body['Price']) + if item.price < 0: + raise ValidationError("Price must be a decimal > 0") + except (ValueError, InvalidOperation): + raise ValidationError("Price must be a decimal > 0") item.discontinued = request.json_body['Discontinued'] transaction.commit() return product_info(item.id) @@ -131,9 +169,10 @@ def product_info(id): else: product = Product.by_id(id) product = {'ProductID': product.id, 'Code': product.code, 'Name': product.name, 'Units': product.units, - 'Fraction': product.fraction, 'FractionUnits': product.fraction_units, 'Yeild': product.yeild, - 'ShowForPurchase': product.show_for_purchase, 'Discontinued': product.discontinued, - 'IsFixture': product.is_fixture, 'ProductGroup': {'ProductGroupID': product.product_group_id}, + 'Fraction': product.fraction, 'FractionUnits': product.fraction_units, + 'ProductYield': product.product_yield, 'ShowForPurchase': product.show_for_purchase, + 'Discontinued': product.discontinued, 'IsFixture': product.is_fixture, + 'ProductGroup': {'ProductGroupID': product.product_group_id}, 'Ledger': {'LedgerID': product.ledger_id}, 'Price': product.price} return product diff --git a/brewman/views/services/batch.py b/brewman/views/services/batch.py index de084a90..70158dea 100644 --- a/brewman/views/services/batch.py +++ b/brewman/views/services/batch.py @@ -6,7 +6,7 @@ from brewman.models.voucher import Batch @view_config(request_method='GET', route_name='api_batch', renderer='json', permission='Authenticated') def batch_term(request): filter = request.GET.get('term', None) - filter = filter if filter is not None and filter is not '' else None + filter = filter if filter is not None and filter.strip() is not '' else None count = request.GET.get('count', None) count = None if count is None or count == '' else int(count) date = request.GET.get('date', None)