Exemple #1
0
    def _get_by_doc(self, store, data, doc):
        # Extra precaution in case we ever send the cpf already formatted
        document = format_cpf(raw_document(doc))

        person = Person.get_by_document(store, document)
        if not person or not person.client:
            return data

        return self._dump_client(person.client)
    def _get_by_doc(self, store, data, doc):
        # Extra precaution in case we ever send the cpf already formatted
        document = format_cpf(raw_document(doc))

        person = Person.get_by_document(store, document)
        if person and person.client:
            data = self._dump_client(person.client)

        # Plugins that listen to this signal will return extra fields
        # to be added to the response
        responses = signal('CheckRewardsPermissionsEvent').send(doc)
        for response in responses:
            data.update(response[1])

        return data
Exemple #3
0
    def get(self, store):
        from stoqnfe.domain.distribution import ImportedNfe

        cnpj = self.get_arg('cnpj')
        limit = self.get_arg('limit')
        offset = self.get_arg('offset')

        if not cnpj:
            message = "'cnpj' not provided"
            log.error(message)
            abort(400, message)

        if not validate_cnpj(cnpj):
            message = "Invalid 'cnpj' provided"
            log.error(message)
            abort(400, message)

        if limit is not None:
            try:
                limit = int(limit)
            except (TypeError, ValueError):
                message = "'limit' must be a number"
                log.error(message)
                abort(400, message)

            if limit > MAX_PAGE_SIZE:
                message = "'limit' must be lower than %s" % MAX_PAGE_SIZE
                log.error(message)
                abort(400, message)

        if offset is not None:
            try:
                offset = int(offset)
            except (TypeError, ValueError):
                message = "'offset' must be a number"
                log.error(message)
                abort(400, message)

        cnpj = format_cnpj(raw_document(cnpj))
        limit = limit or 20
        offset = offset or 0

        login_user = self.get_current_user(store)
        tables = [
            Branch,
            Join(Person, Branch.person_id == Person.id),
            Join(Company, Company.person_id == Person.id)
        ]
        query = Eq(Company.cnpj, cnpj)
        branches = store.using(*tables).find(Branch, query)

        # XXX There should exist at least one branch in database with
        # the cnpj from ImportedNfes. Otherwise, there is something wrong that
        # could lead to unwanted access to these ImportedNfes.
        assert branches

        for branch in branches:
            has_access = UserBranchAccess.has_access(store, login_user, branch)
            if has_access:
                continue

            message = 'login_user %s does not have access to branch %s' % \
                (login_user.id, branch.id)
            log.error(message)
            abort(403, message)

        result = store.find(ImportedNfe,
                            cnpj=cnpj).order_by(Desc(ImportedNfe.te_id))
        result_count = result.count()
        imported_nfes = result.config(offset=offset, limit=limit)

        records = []
        for imported_nfe in imported_nfes:
            # FIXME: Change it to a store.find() when NFePurchase.key had been implemented
            query = "SELECT id FROM nfe_purchase WHERE cnpj='{}' AND xml::text ilike '%{}%'"
            nfe_purchase = store.execute(
                query.format(imported_nfe.cnpj, imported_nfe.key)).get_one()

            process_date = imported_nfe.process_date
            record = {
                'id': imported_nfe.id,
                'key': imported_nfe.key,
                # Since process_date is a new column, we can't assure that
                # all entries have it fulfilled
                'process_date': process_date and process_date.isoformat(),
                'purchase_invoice_id': nfe_purchase and nfe_purchase[0]
            }
            records.append(record)

        next_offset = offset + limit
        has_next = result_count > next_offset
        next_ = None
        if has_next:
            next_ = self.routes[0] + '?limit={}&offset={}&cnpj={}'.format(
                limit, offset + limit, cnpj)

        has_previous = offset > 0
        previous = None
        if has_previous:
            previous = self.routes[0] + '?limit={}&offset={}&cnpj={}'.format(
                limit, max(offset - limit, 0), cnpj)

        response = {
            'previous': previous,
            'next': next_,
            'count': len(records),
            'total_records': result_count,
            'records': records
        }
        return make_response(jsonify(response), 200)
Exemple #4
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
Exemple #5
0
    def get(self, store):
        data = request.args

        required_params = ['dtinicio', 'dtfim']
        _check_required_params(data, required_params)

        initial_date = datetime.datetime.strptime(data['dtinicio'], '%Y-%m-%d')
        end_date = datetime.datetime.combine(
            datetime.datetime.strptime(data['dtfim'], '%Y-%m-%d').date(),
            datetime.time.max)

        request_branches = data.get('lojas')
        request_documents = data.get('consumidores')
        request_invoice_keys = data.get('operacaocupom')

        branch_ids = _parse_request_list(request_branches)
        _check_if_uuid(branch_ids)
        documents = _parse_request_list(request_documents)
        invoice_keys = _parse_request_list(request_invoice_keys)

        clauses = [
            Sale.confirm_date >= initial_date, Sale.confirm_date <= end_date
        ]

        ClientPerson = ClassAlias(Person, 'person_client')
        ClientIndividual = ClassAlias(Individual, 'individual_client')
        ClientCompany = ClassAlias(Company, 'company_client')

        SalesPersonPerson = ClassAlias(Person, 'person_sales_person')
        SalesPersonIndividual = ClassAlias(Individual,
                                           'individual_sales_person')

        tables = [
            Sale,
            Join(Branch, Sale.branch_id == Branch.id),
            LeftJoin(Client, Client.id == Sale.client_id),
            LeftJoin(Person, Person.id == Client.person_id),
            Join(BranchStation, Sale.station_id == BranchStation.id),
            LeftJoin(ClientPerson, Client.person_id == ClientPerson.id),
            LeftJoin(ClientIndividual,
                     Client.person_id == ClientIndividual.person_id),
            LeftJoin(Individual, Client.person_id == Individual.person_id),
            LeftJoin(ClientCompany,
                     Client.person_id == ClientCompany.person_id),
            LeftJoin(Company, Client.person_id == Company.person_id),
            LeftJoin(SalesPerson, SalesPerson.id == Sale.salesperson_id),
            LeftJoin(SalesPersonPerson,
                     SalesPerson.person_id == SalesPersonPerson.id),
            LeftJoin(SalesPersonIndividual,
                     SalesPerson.person_id == SalesPersonIndividual.person_id),
            Join(LoginUser, LoginUser.person_id == SalesPerson.person_id),
            Join(PaymentGroup, PaymentGroup.id == Sale.group_id),
            Join(Invoice, Sale.invoice_id == Invoice.id),
        ]

        payment_tables = [
            Payment,
            Join(PaymentMethod, Payment.method_id == PaymentMethod.id),
            Join(PaymentGroup, Payment.group_id == PaymentGroup.id),
        ]

        sale_objs = (Sale, ClientCompany, ClientIndividual, LoginUser, Branch,
                     PaymentGroup, BranchStation, Client, ClientPerson,
                     SalesPerson, SalesPersonPerson)

        payment_objs = (Payment, PaymentMethod, PaymentGroup)

        if len(branch_ids) > 0:
            clauses.append(Branch.id.is_in(branch_ids))

        if len(documents) > 0:
            clauses.append(
                Or(Individual.cpf.is_in(documents),
                   Company.cnpj.is_in(documents)))

        if len(invoice_keys) > 0:
            clauses.append(Invoice.key.is_in(invoice_keys))

        if data.get('cancelados') and data.get('cancelados') == '0':
            clauses.append(Sale.status != Sale.STATUS_CANCELLED)

        if data.get('cancelados') and data.get('cancelados') == '1':
            clauses.append(Sale.status == Sale.STATUS_CANCELLED)

        data = list(store.using(*tables).find(sale_objs, And(*clauses)))

        group_ids = [i[0].group_id for i in data]

        payments_list = list(
            store.using(*payment_tables).find(
                payment_objs, Payment.group_id.is_in(group_ids)))

        sale_payments = {}
        for payment in payments_list:
            sale_payments.setdefault(payment[0].group_id, [])
            sale_payments[payment[0].group_id].append(payment[0])

        response = []

        for row in data:
            sale, company, individual, login_user, branch, group = row[:6]
            cpf = individual and individual.cpf
            cnpj = company and company.cnpj

            document = cpf or cnpj or ''

            if cpf:
                document_type = 'CPF'
            elif cnpj:
                document_type = 'CNPJ'
            else:
                document_type = ''

            network = _get_network_info()

            payment_methods = _get_payments_info(sale_payments[sale.group_id],
                                                 login_user, sale)
            change = sum(payment['troco'] for payment in payment_methods)

            res_item = {
                'idMovimentoCaixa':
                sale.id,
                'redeId':
                network['id'],
                'rede':
                network['name'],
                'lojaId':
                branch.id,
                'loja':
                branch.name,
                'hora':
                sale.confirm_date.strftime('%H'),
                'cancelado':
                sale.status == Sale.STATUS_CANCELLED,
                'idAtendente':
                login_user.id,
                'codAtendente':
                login_user.username,
                'nomeAtendente':
                sale.salesperson.person.name,
                'vlDesconto':
                float(sale.discount_value),
                'vlAcrescimo':
                float(sale.surcharge_value),
                'vlTotalReceber':
                float(sale.total_amount),
                'vlTotalRecebido':
                float(self._get_payments_sum(sale_payments[sale.group_id])),
                'vlTrocoFormasPagto':
                change,
                'vlServicoRecebido':
                0,
                'vlRepique':
                0,
                'vlTaxaEntrega':
                0,
                'numPessoas':
                1,
                'operacaoId':
                sale.id,
                'maquinaId':
                sale.station.id,
                'nomeMaquina':
                sale.station.name,
                'maquinaCod':
                _get_station_code(sale.station),
                'maquinaPortaFiscal':
                None,
                'meiosPagamento':
                payment_methods,
                'consumidores': [{
                    'documento': raw_document(document),
                    'tipo': document_type,
                }],
                # FIXME B1Food expect this date to be the same as the emission date
                # we want the emission date of nfe_data for this field
                # https://gitlab.com/stoqtech/private/stoq-plugin-nfe/-/issues/111
                'dataContabil':
                sale.confirm_date.strftime('%Y-%m-%d %H:%M:%S -0300'),
                'periodoId':
                None,
                'periodoCod':
                None,
                'periodoNome':
                None,
                'centroRendaId':
                None,
                'centroRendaCod':
                None,
                'centroRendaNome':
                None,
            }

            response.append(res_item)

        return response
Exemple #6
0
    def get(self, store):
        data = request.args

        required_params = ['dtinicio', 'dtfim']
        _check_required_params(data, required_params)

        initial_date = datetime.datetime.strptime(data['dtinicio'], '%Y-%m-%d')
        end_date = datetime.datetime.combine(
            datetime.datetime.strptime(data['dtfim'], '%Y-%m-%d').date(),
            datetime.time.max)

        request_branches = data.get('lojas')
        request_documents = data.get('consumidores')
        request_invoice_keys = data.get('operacaocupom')

        branch_ids = _parse_request_list(request_branches)
        _check_if_uuid(branch_ids)

        documents = _parse_request_list(request_documents)
        invoice_keys = _parse_request_list(request_invoice_keys)

        if data.get('usarDtMov') and data.get('usarDtMov') == '1':
            clauses = [
                Sale.confirm_date >= initial_date,
                Sale.confirm_date <= end_date
            ]
        else:
            clauses = [
                Sale.open_date >= initial_date, Sale.open_date <= end_date
            ]

        ClientPerson = ClassAlias(Person, 'person_client')
        ClientIndividual = ClassAlias(Individual, 'individual_client')
        ClientCompany = ClassAlias(Company, 'company_client')

        SalesPersonPerson = ClassAlias(Person, 'person_sales_person')
        SalesPersonIndividual = ClassAlias(Individual,
                                           'individual_sales_person')

        tables = [
            Sale,
            Join(Branch, Sale.branch_id == Branch.id),
            LeftJoin(Client, Client.id == Sale.client_id),
            LeftJoin(Person, Person.id == Client.person_id),
            Join(BranchStation, Sale.station_id == BranchStation.id),
            LeftJoin(ClientPerson, Client.person_id == ClientPerson.id),
            LeftJoin(ClientIndividual,
                     Client.person_id == ClientIndividual.person_id),
            LeftJoin(Individual, Client.person_id == Individual.person_id),
            LeftJoin(ClientCompany,
                     Client.person_id == ClientCompany.person_id),
            LeftJoin(Company, Client.person_id == Company.person_id),
            LeftJoin(SalesPerson, SalesPerson.id == Sale.salesperson_id),
            LeftJoin(SalesPersonPerson,
                     SalesPerson.person_id == SalesPersonPerson.id),
            LeftJoin(SalesPersonIndividual,
                     SalesPerson.person_id == SalesPersonIndividual.person_id),
            Join(LoginUser, LoginUser.person_id == SalesPerson.person_id),
            Join(Invoice, Sale.invoice_id == Invoice.id),
        ]

        sale_item_tables = [
            SaleItem,
            Join(Sellable, SaleItem.sellable_id == Sellable.id),
            Join(Product, SaleItem.sellable_id == Product.id),
            LeftJoin(SellableCategory,
                     Sellable.category_id == SellableCategory.id),
            LeftJoin(TransactionEntry,
                     SellableCategory.te_id == TransactionEntry.id)
        ]

        sale_objs = (Sale, ClientCompany, ClientIndividual, LoginUser, Branch,
                     BranchStation, Client, ClientPerson, SalesPerson,
                     SalesPersonPerson)

        sale_items_objs = (SaleItem, Sellable, SellableCategory, Product,
                           TransactionEntry)

        if len(branch_ids) > 0:
            clauses.append(Branch.id.is_in(branch_ids))

        if len(documents) > 0:
            clauses.append(
                Or(Individual.cpf.is_in(documents),
                   Company.cnpj.is_in(documents)))

        if len(invoice_keys) > 0:
            clauses.append(Invoice.key.is_in(invoice_keys))

        if data.get('cancelados') and data.get('cancelados') == '0':
            clauses.append(Sale.status != Sale.STATUS_CANCELLED)

        if data.get('cancelados') and data.get('cancelados') == '1':
            clauses.append(Sale.status == Sale.STATUS_CANCELLED)

        data = list(store.using(*tables).find(sale_objs, And(*clauses)))

        sale_ids = [i[0].id for i in data]
        sale_items = list(
            store.using(*sale_item_tables).find(
                sale_items_objs, SaleItem.sale_id.is_in(sale_ids)))

        sales = {}
        for item in sale_items:
            sales.setdefault(item[0].sale_id, [])
            sales[item[0].sale_id].append(item[0])

        response = []

        for row in data:
            sale, company, individual, login_user = row[:4]
            for item in sales[sale.id]:
                discount = item.item_discount
                sellable = item.sellable
                station = sale.station
                salesperson = sale.salesperson

                cpf = individual and individual.cpf
                cnpj = company and company.cnpj

                document = cpf or cnpj or ''

                if cpf:
                    document_type = 'CPF'
                elif cnpj:
                    document_type = 'CNPJ'
                else:
                    document_type = ''

                network = _get_network_info()

                res_item = {
                    'idItemVenda':
                    item.id,
                    'valorUnitario':
                    float(item.base_price),
                    'valorBruto':
                    float(item.base_price * item.quantity),
                    'valorUnitarioLiquido':
                    float(item.price),
                    'valorLiquido':
                    float(item.price * item.quantity),
                    'idOrigem':
                    None,
                    'codOrigem':
                    None,
                    'desconto':
                    float(discount),
                    'acrescimo':
                    0,
                    'maquinaId':
                    station.id,
                    'nomeMaquina':
                    station.name,
                    'maquinaCod':
                    _get_station_code(station),
                    'quantidade':
                    float(item.quantity),
                    'redeId':
                    network['id'],
                    'lojaId':
                    sale.branch.id,
                    'idMaterial':
                    sellable.id,
                    'codMaterial':
                    _get_sellable_code(sellable),
                    'descricao':
                    sellable.description,
                    'grupo':
                    _get_category_info(sellable),
                    'operacaoId':
                    sale.id,
                    'atendenteId':
                    login_user.id,
                    'atendenteCod':
                    login_user.username,
                    'atendenteNome':
                    salesperson.person.name,
                    'isTaxa':
                    False,
                    'isRepique':
                    False,
                    'isGorjeta':
                    False,
                    'isEntrega':
                    False,  # FIXME maybe should be true if external order
                    'consumidores': [{
                        'documento': raw_document(document),
                        'tipo': document_type
                    }],
                    'cancelado':
                    sale.status == Sale.STATUS_CANCELLED,
                    'dtLancamento':
                    sale.confirm_date.strftime('%Y-%m-%d'),
                    'horaLancamento':
                    sale.confirm_date.strftime('%H:%M'),
                    'tipoDescontoId':
                    sale.client_category and sale.client_category.id,
                    'tipoDescontoCod':
                    _get_client_category_code(sale.client_category),
                    'tipoDescontoNome':
                    sale.client_category and sale.client_category.name,
                }

                response.append(res_item)

        return response