Esempio n. 1
0
    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()
Esempio n. 2
0
    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
Esempio n. 3
0
    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
Esempio n. 4
0
 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
Esempio n. 5
0
    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)
Esempio n. 6
0
 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()
Esempio n. 7
0
 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()
Esempio n. 8
0
 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()
Esempio n. 9
0
    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
Esempio n. 10
0
 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)
Esempio n. 11
0
 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)
Esempio n. 12
0
    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
Esempio n. 13
0
 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)