Source code for stoqlib.lib.ibpt

# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4

##
## Copyright (C) 2013 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
"""
Identify and apply to a product, the defined taxes by
IBPT - Instituto Brasileiro de Planejamento Tributário (Brazilian Institute
of Tributary Planning)
According to Law 12,741 of 12/08/2012 - Taxes in Coupon.
"""
from collections import namedtuple

from kiwi.environ import environ

import csv
from decimal import Decimal

from stoqlib.database.runtime import get_current_branch, new_store

taxes_data = {}
TaxInfo = namedtuple('TaxInfo', 'nacionalfederal, importadosfederal, estadual,'
                     'fonte, chave')


[docs]def load_taxes_csv(): """ Load the fields of IBPT table. - Fields: - ncm: Nomenclatura Comum do Sul. - ex: Exceção fiscal da NCM. - tipo: Código que pertence a uma NCM. - descricao: Nome do produto. - nacionalfederal: Carga tributária para os produtos nacionais. - importadosfederal: Carga tributária para os produtos importados. - estadual: Carga tributária estadual - municipal: Carga tributária municipal - vigenciainicio: Data de início da vigência desta alíquota. - vigenciafim: Data de fim da vigência desta alíquota. - chave: Chave que associa a Tabela IBPT baixada com a empresa. - versao: Versão das alíquotas usadas para cálculo. - Fonte: Fonte """ # Avoid load taxes more than once. if taxes_data: return store = new_store() branch = get_current_branch(store) address = branch.person.get_main_address() state = address.city_location.state filename = environ.get_resource_filename('stoq', 'csv', 'ibpt_tables', 'TabelaIBPTax%s.csv' % state) csv_file = (csv.reader(open(filename, "r"), delimiter=';')) for (ncm, ex, tipo, descricao, nacionalfederal, importadosfederal, estadual, municipal, vigenciainicio, vigenciafim, chave, versao, fonte) in csv_file: # Ignore service codes (NBS - Nomenclatura Brasileira de Serviços) if tipo == '1': continue tax_dict = taxes_data.setdefault(ncm, {}) tax_dict[ex] = TaxInfo(nacionalfederal, importadosfederal, estadual, fonte, chave)
[docs]class IBPTGenerator(object): def __init__(self, items, include_services=False): load_taxes_csv() self.items = items self.include_services = include_services def _format_ex(self, ex_tipi): if not ex_tipi: return '' ex = int(ex_tipi) return str(ex).zfill(2) def _load_tax_values(self, item): assert item sellable = item.sellable product = sellable.product service = sellable.service if product: code = product.ncm or '' ex_tipi = self._format_ex(product.ex_tipi) else: if not self.include_services: return code = '%04d' % int(service.service_list_item_code.replace('.', '')) ex_tipi = '' options = taxes_data.get(code, {}) n_options = len(options) if n_options == 0: tax_values = TaxInfo('0', '0', '0', '', '0') elif n_options == 1: tax_values = options[''] else: tax_values = options.get(ex_tipi) or options[''] return tax_values def _calculate_federal_tax(self, item, tax_values): """ Calculate the IBPT tax for a give item. :param item: a |saleitem| :returns: the IBPT tax or ``0`` if it does not exist :rtype: decimal """ if tax_values is None: return Decimal("0") sellable = item.sellable product = sellable.product if product and product.icms_template: origin = product.icms_template.orig else: # If the product does not have any fiscal information or it's a # service, defaults to national origin origin = 0 # Values (0, 3, 4, 5, 8) represent the taxes codes of brazilian origin. if origin in [0, 3, 4, 5, 8]: federal_tax = Decimal(tax_values.nacionalfederal) / 100 # Different codes, represent taxes of international origin. else: federal_tax = Decimal(tax_values.importadosfederal) / 100 total_item = item.price * item.quantity return total_item * federal_tax def _calculate_state_tax(self, item, tax_values): if tax_values is None: return Decimal("0") total_item = item.price * item.quantity state_tax = Decimal(tax_values.estadual) / 100 return total_item * state_tax
[docs] def get_ibpt_message(self): federal_tax = state_tax = 0 for item in self.items: tax_values = self._load_tax_values(item) federal_tax += self._calculate_federal_tax(item, tax_values) state_tax += self._calculate_state_tax(item, tax_values) if tax_values: source = tax_values.fonte key = tax_values.chave else: source = "" key = "0" federal_msg = "%0.2f Federal" % federal_tax state_msg = "%0.2f Estadual" % state_tax final_msg = ("Trib aprox R$: {federal} e {state}\n" "Fonte: {source} {key}") return final_msg.format(federal=federal_msg, state=state_msg, source=source, key=key)
[docs]def generate_ibpt_message(items, include_services=False): generator = IBPTGenerator(items, include_services) return generator.get_ibpt_message()