def _close_till(self, store, till_summaries): self.ensure_printer() # Here till object must exist till = Till.get_last(store) # Create TillSummaries till.get_day_summary() # Find TillSummary and store the user_value for till_summary in till_summaries: method = PaymentMethod.get_by_name(store, till_summary['method']) if till_summary['provider']: provider = store.find(CreditProvider, short_name=till_summary['provider']).one() summary = TillSummary.get_or_create(store, till=till, method=method.id, provider=provider.id, card_type=till_summary['card_type']) # Money method has no card_data or provider else: summary = TillSummary.get_or_create(store, till=till, method=method.id) summary.user_value = decimal.Decimal(till_summary['user_value']) balance = till.get_balance() if balance: till.add_debit_entry(balance, _('Blind till closing')) till.close_till()
def needs_closing(self): """Checks if the last opened till was closed and asks the user if he wants to close it :returns: - CLOSE_TILL_BOTH if both DB and ECF needs closing. - CLOSE_TILL_DB if only DB needs closing. - CLOSE_TILL_ECF if only ECF needs closing. - CLOSE_TILL_NONE if both ECF and DB are consistent (they may be closed, or open for the current day) """ ecf_needs_closing = HasPendingReduceZ.emit() last_till = Till.get_last(self.store) if last_till: db_needs_closing = last_till.needs_closing() else: db_needs_closing = False if db_needs_closing and ecf_needs_closing: return CLOSE_TILL_BOTH elif db_needs_closing and not ecf_needs_closing: return CLOSE_TILL_DB elif ecf_needs_closing and not db_needs_closing: return CLOSE_TILL_ECF else: return CLOSE_TILL_NONE
def needs_closing(self): """Checks if the last opened till was closed and asks the user if he wants to close it :returns: - CLOSE_TILL_BOTH if both DB and ECF needs closing. - CLOSE_TILL_DB if only DB needs closing. - CLOSE_TILL_ECF if only ECF needs closing. - CLOSE_TILL_NONE if both ECF and DB are consistent (they may be closed, or open for the current day) """ ecf_needs_closing = HasPendingReduceZ.emit() last_till = Till.get_last(self.store) if last_till: db_needs_closing = last_till.needs_closing() else: db_needs_closing = False if db_needs_closing and ecf_needs_closing: return CLOSE_TILL_BOTH elif db_needs_closing and not ecf_needs_closing: return CLOSE_TILL_DB elif ecf_needs_closing and not db_needs_closing: return CLOSE_TILL_ECF else: return CLOSE_TILL_NONE
def _open_till(self, store, initial_cash_amount=0): station = get_current_station(store) last_till = Till.get_last(store) if not last_till or last_till.status != Till.STATUS_OPEN: # Create till and open till = Till(store=store, station=station) till.open_till() till.initial_cash_amount = decimal.Decimal(initial_cash_amount) else: # Error, till already opened assert False
def _add_credit_or_debit_entry(self, store, data): # Here till object must exist till = Till.get_last(store) user = api.get_current_user(store) # FIXME: Check balance when removing to prevent negative till. if data['operation'] == 'debit_entry': reason = _('The user %s removed cash from till') % user.username till.add_debit_entry(decimal.Decimal(data['entry_value']), reason) elif data['operation'] == 'credit_entry': reason = _('The user %s supplied cash to the till') % user.username till.add_credit_entry(decimal.Decimal(data['entry_value']), reason)
def __init__(self, store, model=None, previous_day=False, close_db=True, close_ecf=True): """ Create a new TillClosingEditor object. :param previous_day: If the till wasn't closed previously """ self._previous_day = previous_day self.till = Till.get_last(store) if close_db: assert self.till self._close_db = close_db self._close_ecf = close_ecf BaseEditor.__init__(self, store, model) self._setup_widgets()
def __init__(self, store, model=None, previous_day=False, close_db=True, close_ecf=True): """ Create a new TillClosingEditor object. :param previous_day: If the till wasn't closed previously """ self._previous_day = previous_day self.till = Till.get_last(store) if close_db: assert self.till self._close_db = close_db self._close_ecf = close_ecf BaseEditor.__init__(self, store, model) self._setup_widgets()
def __init__(self, store, model=None, previous_day=False, close_db=True, close_ecf=True): """ Create a new TillClosingEditor object. :param previous_day: If the till wasn't closed previously """ self._previous_day = previous_day self.till = Till.get_last(store, api.get_current_station(store)) if close_db: assert self.till self._close_db = close_db self._close_ecf = close_ecf self._blind_close = sysparam.get_bool('TILL_BLIND_CLOSING') BaseEditor.__init__(self, store, model) self._setup_widgets()
def get(self): # Retrieve Till data with api.new_store() as store: till = Till.get_last(store) if not till: return None till_data = { 'status': till.status, 'opening_date': till.opening_date.strftime('%Y-%m-%d'), 'closing_date': (till.closing_date.strftime('%Y-%m-%d') if till.closing_date else None), 'initial_cash_amount': str(till.initial_cash_amount), 'final_cash_amount': str(till.final_cash_amount), # Get payments data that will be used on 'close_till' action. 'entry_types': till.status == 'open' and self._get_till_summary(store, till) or [], } return till_data
def testGetLast(self): till = Till(store=self.store, station=get_current_station(self.store)) till.open_till() self.assertEquals(Till.get_last(self.store), till)
def test_get_last(self): till = Till(store=self.store, station=get_current_station(self.store)) till.open_till() self.assertEqual(Till.get_last(self.store), till)
def post(self, store): # FIXME: Check branch state and force fail if no override for that product is present. self.ensure_printer() data = request.get_json() client_id = data.get('client_id') products = data['products'] payments = data['payments'] client_category_id = data.get('price_table') document = raw_document(data.get('client_document', '') or '') if document: document = format_document(document) if client_id: client = store.get(Client, client_id) elif document: person = Person.get_by_document(store, document) client = person and person.client else: client = None # Create the sale branch = api.get_current_branch(store) group = PaymentGroup(store=store) user = api.get_current_user(store) sale = Sale( store=store, branch=branch, salesperson=user.person.sales_person, client=client, client_category_id=client_category_id, group=group, open_date=localnow(), coupon_id=None, ) # Add products for p in products: sellable = store.get(Sellable, p['id']) item = sale.add_sellable(sellable, price=currency(p['price']), quantity=decimal.Decimal(p['quantity'])) # XXX: bdil has requested that when there is a special discount, the discount does # not appear on the coupon. Instead, the item wil be sold using the discount price # as the base price. Maybe this should be a parameter somewhere item.base_price = item.price # Add payments sale_total = sale.get_total_sale_amount() money_payment = None payments_total = 0 for p in payments: method_name = p['method'] tef_data = p.get('tef_data', {}) if method_name == 'tef': p['provider'] = tef_data['card_name'] method_name = 'card' method = PaymentMethod.get_by_name(store, method_name) installments = p.get('installments', 1) or 1 due_dates = list(create_date_interval( INTERVALTYPE_MONTH, interval=1, start_date=localnow(), count=installments)) payment_value = currency(p['value']) payments_total += payment_value p_list = method.create_payments( Payment.TYPE_IN, group, branch, payment_value, due_dates) if method.method_name == 'money': # FIXME Frontend should not allow more than one money payment. this can be changed # once https://gitlab.com/stoqtech/private/bdil/issues/75 is fixed? if not money_payment or payment_value > money_payment.value: money_payment = p_list[0] elif method.method_name == 'card': for payment in p_list: card_data = method.operation.get_card_data_by_payment(payment) card_type = p['mode'] # Stoq does not have the voucher comcept, so register it as a debit card. if card_type == 'voucher': card_type = 'debit' device = self._get_card_device(store, 'TEF') provider = self._get_provider(store, p['provider']) if tef_data: card_data.nsu = tef_data['aut_loc_ref'] card_data.auth = tef_data['aut_ext_ref'] card_data.update_card_data(device, provider, card_type, installments) card_data.te.metadata = tef_data # If payments total exceed sale total, we must adjust money payment so that the change is # correctly calculated.. if payments_total > sale_total and money_payment: money_payment.value -= (payments_total - sale_total) assert money_payment.value >= 0 # Confirm the sale group.confirm() sale.order() till = Till.get_last(store) sale.confirm(till) # Fiscal plugins will connect to this event and "do their job" # It's their responsibility to raise an exception in case of # any error, which will then trigger the abort bellow # FIXME: Catch printing errors here and send message to the user. SaleConfirmedRemoteEvent.emit(sale, document) # This will make sure we update any stock or price changes products may # have between sales return True
def test_get_last(self): till = Till(store=self.store, branch=self.current_branch, station=self.current_station) till.open_till(self.current_user) self.assertEqual(Till.get_last(self.store, self.current_station), till)