def stock_info(item): auth = current.auth T = current.T session = current.session available = True stock = 0 if auth.has_membership('Employee') and item.has_inventory: stock = item_stock_qty(item, session.store) stock = fix_item_quantity(item, stock) if stock <= 0: stock = SPAN(T('Out of stock'), _class="text-danger") available = False else: stock = str(stock) + " %s %s " % (item.id_measure_unit.symbol, T('Available')) else: stock = item_stock_qty(item) if stock <= 0: stock = SPAN(T('Out of stock'), _class="text-danger") available = False else: stock = SPAN(T('Available'), _class="text-success") return stock
def modify_bag_item(): """ modifies the bag_item quantity. args: [ bag_item ] """ bag_item = db.bag_item(request.args(0)) if not bag_item: raise HTTP(404) bag_utils.is_modifiable_bag(bag_item.id_bag) old_qty = bag_item.quantity bag_item.quantity = request.vars.quantity if request.vars.quantity else bag_item.quantity if not bag_item.id_item.allow_fractions: bag_item.quantity = remove_fractions(bag_item.quantity) bag_item.quantity = DQ(bag_item.quantity) if not allow_out_of_stock: qty = item_utils.item_stock_qty( db.item(bag_item.id_item), session.store, id_bag=session.current_bag ) diff = (old_qty - bag_item.quantity) if (old_qty - bag_item.quantity) > 0 else 0 if qty + diff < bag_item.quantity - old_qty: bag_item.quantity = max(old_qty, qty + old_qty) bag_item.quantity = max(0, bag_item.quantity) bag_item.update_record() bag_data = bag_utils.refresh_bag_data(bag_item.id_bag.id) return dict(status='ok', bag_item=bag_item, **bag_data)
def modify_bag_item(): """ modifies the bag_item quantity. args: [ bag_item ] """ bag_item = db.bag_item(request.args(0)) bag_utils.is_modifiable_bag(bag_item.id_bag) if not bag_item: raise HTTP(404) old_qty = bag_item.quantity bag_item.quantity = request.vars.quantity if request.vars.quantity else bag_item.quantity if not bag_item.id_item.allow_fractions: bag_item.quantity = remove_fractions(bag_item.quantity) bag_item.quantity = DQ(bag_item.quantity) if not allow_out_of_stock: qty = item_utils.item_stock_qty( db.item(bag_item.id_item), session.store, id_bag=session.current_bag ) diff = (old_qty - bag_item.quantity) if (old_qty - bag_item.quantity) > 0 else 0 if qty + diff < bag_item.quantity - old_qty: bag_item.quantity = max(old_qty, qty + old_qty) bag_item.quantity = max(0, bag_item.quantity) bag_item.update_record() bag_data = bag_utils.refresh_bag_data(bag_item.id_bag.id) return dict(status='ok', bag_item=bag_item, **bag_data)
def out_of_stock_items_exists(bag_items, allow_out_of_stock): if allow_out_of_stock: return False for bag_item in bag_items: qty = item_utils.item_stock_qty(bag_item.id_item, bag_item.id_bag.id_store.id) if bag_item.quantity > qty: return True return False
def out_of_stock_items_exists(bag_items, allow_out_of_stock): if allow_out_of_stock: return False for bag_item in bag_items: qty = item_utils.item_stock_qty( bag_item.id_item, bag_item.id_bag.id_store.id ) if bag_item.quantity > qty: return True return False
def add_bag_item(bag, item, quantity=None, sale_price=None): """ """ db = current.db sale_price = sale_price if sale_price else item.base_price bag_item = db((db.bag_item.id_item == item.id) & (db.bag_item.id_bag == bag.id)).select().first() # when quantity is not specified, avoid stock checking (the API user knows what he is doing) if not quantity: stock_qty = item_utils.item_stock_qty(item, bag.id_store, id_bag=bag.id) if item.has_inventory: stock_qty = DQ(stock_qty) base_qty = base_qty = 1 if stock_qty >= 1 or ALLOW_OUT_OF_STOCK else stock_qty % 1 # modulo to consider fractionary items # if there is no stock notify the user if base_qty <= 0: raise CP_OutOfStockError() quantity = base_qty # create item taxes string, the string contains the tax name and its percentage, see db.py > bag_item table for more info if not bag_item: item_taxes_str = '' for tax in item.taxes: item_taxes_str += '%s:%s' % (tax.name, tax.percentage) if tax != item.taxes[-1]: item_taxes_str += ',' discounts = item_utils.item_discounts(item) sale_price = item_utils.discount_data(discounts, sale_price)[0] discount = item.base_price - sale_price id_bag_item = db.bag_item.insert(id_bag=bag.id, id_item=item.id, quantity=quantity, sale_price=sale_price, discount=discount, product_name=item.name, item_taxes=item_taxes_str, sale_taxes=item_utils.item_taxes( item, sale_price)) bag_item = db.bag_item(id_bag_item) else: bag_item.quantity += base_qty bag_item.update_record() return bag_item
def add_bag_item(bag, item, quantity=None, sale_price=None): """ """ db = current.db sale_price = sale_price if sale_price else item.base_price bag_item = db( (db.bag_item.id_item == item.id) & (db.bag_item.id_bag == bag.id) ).select().first() # when quantity is not specified, avoid stock checking (the API user knows what he is doing) if not quantity: stock_qty = item_utils.item_stock_qty(item, bag.id_store, id_bag=bag.id) if item.has_inventory: stock_qty = DQ(stock_qty) base_qty = base_qty = 1 if stock_qty >= 1 or ALLOW_OUT_OF_STOCK else stock_qty % 1 # modulo to consider fractionary items # if there is no stock notify the user if base_qty <= 0: raise CP_OutOfStockError() quantity = base_qty # create item taxes string, the string contains the tax name and its percentage, see db.py > bag_item table for more info if not bag_item: item_taxes_str = '' for tax in item.taxes: item_taxes_str += '%s:%s' % (tax.name, tax.percentage) if tax != item.taxes[-1]: item_taxes_str += ',' discounts = item_utils.item_discounts(item) sale_price = item_utils.discount_data(discounts, sale_price)[0] discount = item.base_price - sale_price id_bag_item = db.bag_item.insert( id_bag=bag.id, id_item=item.id, quantity=quantity, sale_price=sale_price, discount=discount, product_name=item.name, item_taxes=item_taxes_str, sale_taxes=item_utils.item_taxes(item, sale_price), reward_points=item.reward_points or 0 ) bag_item = db.bag_item(id_bag_item) else: bag_item.quantity += base_qty bag_item.update_record() return bag_item
def set_bag_item(bag_item, discounts=None): """ modifies bag item data, in order to display it properly, this method does not modify the database """ if discounts is None: discounts = [] session = current.session item = bag_item.id_item bag_item.product_name = item.name + " " + item_utils.concat_traits(item) if discounts is None: discounts = [] # stores the price without discounts real_price = bag_item.sale_price + (bag_item.discount or 0) # discount percentage discount_p = 0 try: discount_p = DQ(1.0) - bag_item.sale_price / real_price except: pass item.base_price -= item.base_price * discount_p bag_item.total_sale_price = str( DQ(bag_item.sale_price + bag_item.sale_taxes, True)) bag_item.base_price = money_format(DQ(item.base_price, True)) if item.base_price else 0 bag_item.price2 = money_format( DQ(item.price2 - item.price2 * discount_p, True)) if item.price2 else 0 bag_item.price3 = money_format( DQ(item.price3 - item.price3 * discount_p, True)) if item.price3 else 0 bag_item.sale_price = money_format(DQ(bag_item.sale_price or 0, True)) # add taxes without discounts real_price += bag_item_taxes(bag_item, real_price) bag_item.price_no_discount = real_price bag_item.measure_unit = item.id_measure_unit.symbol bag_item.barcode = item_utils.item_barcode(item) bag_item.stock = item_utils.item_stock_qty(item, session.store) bag_item.has_inventory = item.has_inventory bag_item.discount_percentage = int(discount_p * D(100.0)) bag_item.real_price = bag_item.sale_price return bag_item
def set_bag_item(bag_item, discounts=None): """ modifies bag item data, in order to display it properly, this method does not modify the database """ if discounts is None: discounts = [] session = current.session item = bag_item.id_item bag_item.product_name = item.name + " " + item_utils.concat_traits(item) if discounts is None: discounts = [] # stores the price without discounts real_price = bag_item.sale_price + (bag_item.discount or 0) # discount percentage discount_p = 0 try: discount_p = DQ(1.0) - bag_item.sale_price / real_price except: pass item.base_price -= item.base_price * discount_p bag_item.total_sale_price = str(DQ(bag_item.sale_price + bag_item.sale_taxes, True)) bag_item.base_price = money_format(DQ(item.base_price, True)) if item.base_price else 0 bag_item.price2 = money_format(DQ(item.price2 - item.price2 * discount_p, True)) if item.price2 else 0 bag_item.price3 = money_format(DQ(item.price3 - item.price3 * discount_p, True)) if item.price3 else 0 bag_item.sale_price = money_format(DQ(bag_item.sale_price or 0, True)) # add taxes without discounts real_price += bag_item_taxes(bag_item, real_price) bag_item.price_no_discount = real_price bag_item.measure_unit = item.id_measure_unit.symbol bag_item.barcode = item_utils.item_barcode(item) bag_item.stock = item_utils.item_stock_qty(item, session.store) bag_item.has_inventory = item.has_inventory bag_item.discount_percentage = int(discount_p * D(100.0)) bag_item.real_price = bag_item.sale_price return bag_item
def check_bag_items_integrity(bag_items, allow_out_of_stock=False): """ verify item stocks and remove unnecessary items """ session = current.session db = current.db auth = current.auth T = current.T out_of_stock_items = [] for bag_item in bag_items: # delete bag item when the item has 0 quantity if bag_item.quantity <= 0: db(db.bag_item.id == bag_item.id).delete() qty = item_utils.item_stock_qty(bag_item.id_item, session.store) if bag_item.quantity > qty and not allow_out_of_stock: out_of_stock_items.append(bag_item) if out_of_stock_items and auth.has_membership('Employee'): session.flash = T('Some items are out of stock or are inconsistent') auto_bag_selection() redirection()
def item_card(item): """ """ session = current.session auth = current.auth db = current.db T = current.T available = "Not available" available_class = "label label-danger" stock_qty = item_stock_qty(item, session.store) if stock_qty > 0: available_class = "label label-success" available = "Available" item_options = DIV() bg_style = "" images = db((db.item_image.id_item == db.item.id) & (db.item.id == item.id) & (db.item.is_active == True)).select(db.item_image.ALL) if images: bg_style = "background-image: url(%s);" % URL( 'static', 'uploads/' + images.first().sm) else: bg_style = "background-image: url(%s);" % URL('static', 'images/no_image.svg') brand_link = H4( A(item.id_brand.name, _href=URL('item', 'browse', vars=dict( brand=item.id_brand.id)))) if item.id_brand else H4( T('No brand')) item_price = (item.base_price or 0) + item_taxes(item, item.base_price) fix_item_price(item, item.base_price) item_price = item.discounted_price item_price_html = DIV() if item.discount_percentage > 0: item_price_html.append( DIV( T('before'), SPAN('$ ', SPAN(item.new_price, _class="old-price"), _class="right"))) item_price_html.append( DIV( T('discount'), SPAN(SPAN(item.discount_percentage), '%', _class="right text-danger"))) item_price_html.append( DIV(SPAN(T(available), _class=available_class + ' item-available'), H4('$ ', DQ(item_price, True), _class="item-price"), _class="item-card-bottom")) # concatenate all the item traits, this string will be appended to the item name traits_str = '' traits_ids = '' item_url = URL('item', 'get_item', args=item.id) item_name = item.name if item.traits: for trait in item.traits: traits_ids += str(trait.id) traits_str += trait.trait_option + ' ' if trait != item.traits[-1]: traits_ids += ',' item_url = URL('item', 'get_item', vars=dict(name=item.name, traits=traits_ids)) item_name = item.name + ' ' + traits_str elif item.description: item_name += ' - ' + item.description[:10] if len(item.description) > 10: item_name += '...' main_content = DIV(H4(A(item_name, _href=item_url)), brand_link, _class="item_data") # item options item_options = DIV(BUTTON(ICON('shopping_basket'), _type="button", _class="btn btn-default", _onclick="add_bag_item(%s)" % item.id), _class="btn-group item-options") if auth.has_membership('Employee'): main_content.append( P('# ', SPAN(item_barcode(item)), _class="item-barcode")) expand_btn = BUTTON(ICON('more_vert'), _type="button", _class="btn btn-default dropdown-toggle", data={'toggle': 'dropdown'}) item_options.append(expand_btn) options_ul = UL(_class="dropdown-menu") if auth.has_membership('Items info') or auth.has_membership( 'Items management') or auth.has_membership('Items prices'): options_ul.append( LI(A(T('Update'), _href=URL('item', 'update', args=item.id)))) options_ul.append( LI( A(T('Print labels'), _href=URL('item', 'labels', args=item.id)))) options_ul.append( LI( A(T('Add images'), _href=URL('item_image', 'create', args=item.id)))) if auth.has_membership('Analytics'): options_ul.append( LI( A(T('Analysis'), _href=URL('analytics', 'item_analysis', args=item.id)))) item_options.append(options_ul) return DIV(A('', _class="panel-heading", _style=bg_style, _href=item_url), DIV(main_content, item_options, item_price_html, _class="panel-body"), _class="panel panel-default item-card")
def item_card(item): """ """ session = current.session auth = current.auth db = current.db T = current.T available = "Not available" available_class = "label label-danger" stock_qty = item_stock_qty(item, session.store) if stock_qty > 0: available_class = "label label-success" available = "Available" item_options = DIV() bg_style = "" images = db( (db.item_image.id_item == db.item.id) & (db.item.id == item.id) & (db.item.is_active == True) ).select(db.item_image.ALL) if images: bg_style = "background-image: url(%s);" % URL('static','uploads/'+ images.first().sm) else: bg_style = "background-image: url(%s);" % URL('static', 'images/no_image.svg') brand_link = H4( A(item.id_brand.name, _href=URL('item', 'browse', vars=dict(brand=item.id_brand.id)) ) ) if item.id_brand else H4(T('No brand')) item_price = (item.base_price or 0) + item_taxes(item, item.base_price) fix_item_price(item, item.base_price) item_price = item.discounted_price item_price_html = DIV() if item.discount_percentage > 0: item_price_html.append(DIV(T('before'), SPAN('$ ', SPAN(item.new_price, _class="old-price"), _class="right"))) item_price_html.append( DIV(T('discount'), SPAN(SPAN(item.discount_percentage), '%', _class="right text-danger")) ) item_price_html.append( DIV( SPAN(T(available), _class=available_class + ' item-available'), H4('$ ', DQ(item_price, True), _class="item-price"), _class="item-card-bottom" ) ) # concatenate all the item traits, this string will be appended to the item name traits_str = '' traits_ids = '' item_url = URL('item', 'get_item', args=item.id) item_name = item.name if item.traits: for trait in item.traits: traits_ids += str(trait.id) traits_str += trait.trait_option + ' ' if trait != item.traits[-1]: traits_ids += ',' item_url = URL('item', 'get_item', vars=dict(name=item.name, traits=traits_ids)) item_name = item.name + ' ' + traits_str elif item.description: item_name += ' - ' + item.description[:10] if len(item.description) > 10: item_name += '...' main_content = DIV( H4(A(item_name, _href=item_url)), brand_link, _class="item_data" ) # item options item_options = DIV( BUTTON(ICON('shopping_basket'), _type="button", _class="btn btn-default", _onclick="add_bag_item(%s)" % item.id) , _class="btn-group item-options" ) if auth.has_membership('Employee'): main_content.append( P('# ', SPAN(item_barcode(item)), _class="item-barcode") ) expand_btn = BUTTON(ICON('more_vert'), _type="button", _class="btn btn-default dropdown-toggle", data={'toggle':'dropdown'}) item_options.append(expand_btn) options_ul = UL(_class="dropdown-menu") if auth.has_membership('Items info') or auth.has_membership('Items management') or auth.has_membership('Items prices'): options_ul.append( LI(A(T('Update'), _href=URL('item', 'update', args=item.id))) ) options_ul.append( LI(A(T('Print labels'), _href=URL('item', 'labels', args=item.id))) ) options_ul.append( LI(A(T('Add images'), _href=URL('item_image', 'create', args=item.id))) ) if auth.has_membership('Analytics'): options_ul.append( LI(A(T('Analysis'), _href=URL('analytics', 'item_analysis', args=item.id))) ) item_options.append(options_ul) return DIV( A('', _class="panel-heading", _style=bg_style, _href=item_url), DIV( main_content, item_options, item_price_html, _class="panel-body" ), _class="panel panel-default item-card" )
def complete(sale): """ """ db = current.db T = current.T payments = db(db.payment.id_sale == sale.id).select() verify_payments(payments, sale) affinity_list = [] # verify items stock bag_items = db(db.bag_item.id_bag == sale.id_bag.id).iterselect() requires_serials = False #TODO implement serial numbers for bag_item in bag_items: # since created bags does not remove stock, there could be more bag_items than stock items, so we need to check if theres enough stock to satisfy this sale, and if there is not, then we need to notify the seller or user stock_qty = item_utils.item_stock_qty(bag_item.id_item, sale.id_store.id, bag_item.id_bag.id) # this has to be done since using item_stock_qty with a bag specified will # consider bagged items as missing, thus we have to add them back, # item_stock_qty must be called with a bag because that way it will # count bundled items with the specified item stock_qty += bag_item.quantity # Cannot deliver a sale with out of stock items if stock_qty < bag_item.quantity: raise CP_OutOfStockError( T("You can't create a counter sale with out of stock items")) requires_serials |= bag_item.id_item.has_serial_number or False affinity_list.append(bag_item.id_item.id) # kinda crappy first = affinity_list[0] for i, id in enumerate(affinity_list): if i == len(affinity_list) - 1: continue for j, other_id in enumerate(affinity_list): if j <= i: continue first, second = (id, other_id) if id < other_id else (other_id, id) affinity = db((db.item_affinity.id_item1 == first) & ( db.item_affinity.id_item2 == second)).select().first() if not affinity: db.item_affinity.insert(id_item1=first, id_item2=second, affinity=1) else: affinity.affinity += 1 affinity.update_record() # for every payment with a payment option with credit days, set payment to not settled for payment in payments: if payment.id_payment_opt.credit_days > 0: payment.epd = date(request.now.year, request.now.month, request.now.day) + timedelta( days=payment.id_payment_opt.credit_days) payment.is_settled = False payment.update_record() store = db(db.store.id == sale.id_store.id).select(for_update=True).first() sale.consecutive = store.consecutive sale.is_done = True create_sale_event(sale, SALE_PAID) sale.update_record() store.consecutive += 1 store.update_record() # if defered sale, remove sale order db(db.sale_order.id_sale == sale.id).delete() # add reward points to the client's wallet, we assume that the user has a wallet if sale.id_client and sale.id_client.id_wallet: wallet = db(db.wallet.id == sale.id_client.id_wallet.id).select( for_update=True).first() wallet.balance += sale.reward_points wallet.update_record()
def complete(sale): """ """ db = current.db T = current.T payments = db(db.payment.id_sale == sale.id).select() verify_payments(payments, sale) affinity_list = [] # verify items stock bag_items = db(db.bag_item.id_bag == sale.id_bag.id).iterselect() requires_serials = False #TODO implement serial numbers for bag_item in bag_items: # since created bags does not remove stock, there could be more bag_items than stock items, so we need to check if theres enough stock to satisfy this sale, and if there is not, then we need to notify the seller or user stock_qty = item_utils.item_stock_qty( bag_item.id_item, sale.id_store.id, bag_item.id_bag.id ) # this has to be done since using item_stock_qty with a bag specified will # consider bagged items as missing, thus we have to add them back, # item_stock_qty must be called with a bag because that way it will # count bundled items with the specified item stock_qty += bag_item.quantity # Cannot deliver a sale with out of stock items if stock_qty < bag_item.quantity: raise CP_OutOfStockError( T("You can't create a counter sale with out of stock items") ) requires_serials |= bag_item.id_item.has_serial_number or False affinity_list.append(bag_item.id_item.id) # kinda crappy first = affinity_list[0] for i, id in enumerate(affinity_list): if i == len(affinity_list) - 1: continue for j, other_id in enumerate(affinity_list): if j <= i: continue first, second = (id, other_id) if id < other_id else (other_id, id) affinity = db( (db.item_affinity.id_item1 == first) & (db.item_affinity.id_item2 == second) ).select().first() if not affinity: db.item_affinity.insert( id_item1=first, id_item2=second, affinity=1 ) else: affinity.affinity += 1 affinity.update_record() # for every payment with a payment option with credit days, set payment to not settled for payment in payments: if payment.id_payment_opt.credit_days > 0: payment.epd = date(request.now.year, request.now.month, request.now.day) + timedelta(days=payment.id_payment_opt.credit_days) payment.is_settled = False payment.update_record() store = db(db.store.id == sale.id_store.id).select(for_update=True).first() sale.consecutive = store.consecutive sale.is_done = True create_sale_event(sale, SALE_PAID) sale.update_record() store.consecutive += 1 store.update_record() # if defered sale, remove sale order db(db.sale_order.id_sale == sale.id).delete() # add reward points to the client's wallet, we assume that the user has a wallet if sale.id_client and sale.id_client.id_wallet: wallet_utils.transaction( sale.reward_points, wallet_utils.CONCEPT_SALE_REWARD, ref=sale.id, wallet_id=sale.id_client.id_wallet.id )