Release 1.0.0
This commit is contained in:
+427
@@ -0,0 +1,427 @@
|
||||
from pprint import pprint
|
||||
from translations import Translations
|
||||
from localconfig import *
|
||||
import re
|
||||
import itertools
|
||||
|
||||
vrbl = Translations()
|
||||
|
||||
def before_tax(grossAmount, taxPercentage):
|
||||
return round(grossAmount / (100.0 + taxPercentage) * 100.0, 2)
|
||||
|
||||
class BaseInvoice():
|
||||
def __init__(self, nr, country):
|
||||
self.nr = nr
|
||||
self.process_country = country
|
||||
self.buyer = None
|
||||
self.seller = None
|
||||
self.receipt = None
|
||||
self.header = None
|
||||
self.totals = None
|
||||
self.tenders = []
|
||||
self.articles = []
|
||||
self.taxes = []
|
||||
self.poslogid = ''
|
||||
|
||||
def add_receipt(self, receipt):
|
||||
self.receipt = receipt
|
||||
|
||||
def add_header(self, header):
|
||||
self.header = header
|
||||
|
||||
def add_totals(self, totals):
|
||||
totals.set_parent(self)
|
||||
self.totals = totals
|
||||
|
||||
def add_buyer(self, organization, source):
|
||||
if organization is not None:
|
||||
organization.set_source(source)
|
||||
organization.set_parent(self)
|
||||
self.buyer = organization
|
||||
|
||||
def add_seller(self, organization):
|
||||
self.seller = organization
|
||||
|
||||
def add_tax(self, tax):
|
||||
tax.set_parent(self)
|
||||
self.taxes.append(tax)
|
||||
|
||||
def total_taxes(self):
|
||||
total = 0.0
|
||||
for t in self.taxes:
|
||||
total += t.amount
|
||||
return total
|
||||
|
||||
def add_article(self, article):
|
||||
article.set_parent(self)
|
||||
self.articles.append(article)
|
||||
|
||||
def add_tender(self, tender):
|
||||
tender.set_parent(self)
|
||||
self.tenders.append(tender)
|
||||
|
||||
def get_total_lines_netsalesprice(self):
|
||||
total = 0.0
|
||||
for a in self.articles:
|
||||
total += a.netSalesPrice()
|
||||
return total
|
||||
|
||||
def roundingAllowed(self):
|
||||
if self.seller:
|
||||
return self.seller.roundingAllowed
|
||||
return 0
|
||||
|
||||
def fuck_prices_of_first_item_to_avoid_rounding(self, roundingamount):
|
||||
log(f"Receipt {self.receipt.id()}: Avoid rounding of {roundingamount:.2f} by updating max {len(self.articles)} articles.")
|
||||
article_cycle = itertools.cycle(self.articles)
|
||||
cents_to_round = int(round((roundingamount * 100),0))
|
||||
number_of_tries = cents_to_round + len(self.articles)
|
||||
for a in article_cycle:
|
||||
# print(f" === {cents_to_round} {number_of_tries}")
|
||||
cents_to_round -= a.increasePrices((cents_to_round / abs(cents_to_round)))
|
||||
number_of_tries -= 1
|
||||
if cents_to_round == 0 or number_of_tries == 0:
|
||||
break
|
||||
|
||||
|
||||
|
||||
def currency(self):
|
||||
return self.header.currency
|
||||
|
||||
def country(self):
|
||||
return self.header.country
|
||||
|
||||
def receipt_id(self):
|
||||
return self.receipt.id()
|
||||
|
||||
|
||||
# def update_currency(self, newcurrency):
|
||||
# self.currency = newcurrency
|
||||
# # if self.sales:
|
||||
# # self.sales.update_currency(newcurrency)
|
||||
|
||||
def __str__(self):
|
||||
result = f"==={'CREDITNOTE ' if self.debit else 'SALE'}=================================================\n"
|
||||
result += f"{self.nr} '{self.storename}' {self.sales}\n"
|
||||
if self.seller:
|
||||
result += str(self.seller)
|
||||
else:
|
||||
result += "[ == no country / PVH-ofice found ==]\n"
|
||||
if self.buyer:
|
||||
result += str(self.buyer)
|
||||
for a in self.articles:
|
||||
result += str(a)
|
||||
for t in self.tenders:
|
||||
result += str(t)
|
||||
for x in self.taxes:
|
||||
result += str(x)
|
||||
return result
|
||||
|
||||
class BaseDiscount():
|
||||
"""
|
||||
Docstring for Discount
|
||||
"""
|
||||
def __init__(self, idtext, action, amount, reasoncode):
|
||||
self.promotionID = idtext
|
||||
self.action = action #substract or add
|
||||
self.amount = float(amount)
|
||||
self.reasoncode = reasoncode
|
||||
self.sales = None
|
||||
self.parent = None
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def currency(self):
|
||||
return self.parent.currency()
|
||||
|
||||
def net_amount(self):
|
||||
return before_tax(self.amount, self.parent.taxpercentage())
|
||||
|
||||
def __str__(self):
|
||||
return(f" {self.promotionID} {self.action} {self.amount} {self.reasoncode}\n")
|
||||
|
||||
class BaseTax():
|
||||
"""
|
||||
Docstring for tax
|
||||
"""
|
||||
spaces = ' '
|
||||
|
||||
def __init__(self, typesubtype, code, amount, percentage, group, taxincludedamount):
|
||||
self.typesubtype = typesubtype
|
||||
self.code = code
|
||||
self.amount = float(amount)
|
||||
self.percentage = float(percentage)
|
||||
self.group = group # GK / SAP taxcodes
|
||||
self.taxincludedamount = float(taxincludedamount)
|
||||
self.taxableamount = self.taxincludedamount - self.amount # net price
|
||||
self.parent = None
|
||||
|
||||
def translated_group(self):
|
||||
return vrbl.translate(self.group, type='tax', country=self.country())
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def currency(self):
|
||||
return self.parent.currency()
|
||||
|
||||
def country(self):
|
||||
return self.parent.country()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.spaces}{self.typesubtype} {self.code} {self.amount} {self.percentage}% {self.translated_group()} {self.taxableamount}\n"
|
||||
|
||||
|
||||
class BaseTotalTax(BaseTax):
|
||||
'''
|
||||
Docstring for Totaltax
|
||||
'''
|
||||
pass
|
||||
|
||||
class BaseHeader():
|
||||
"""
|
||||
will hold the invoice header info
|
||||
"""
|
||||
|
||||
def __init__(self, country, brand, curreny, is_debit, is_return, is_postvoided, loyalty_nr, fiscalseq, is_fiscal, fiscalprinter):
|
||||
self.country = country
|
||||
self.brand = brand
|
||||
self.currency = curreny
|
||||
self.is_debit = is_debit
|
||||
self.is_return = is_return
|
||||
self.is_voided = is_postvoided
|
||||
self.loyalty = loyalty_nr
|
||||
self.fiscalseq = fiscalseq
|
||||
self.is_fiscal = is_fiscal
|
||||
self.fiscalprinter = fiscalprinter
|
||||
self.taxref = {}
|
||||
|
||||
def add_taxref(self, key, value):
|
||||
self.taxref[key] = value
|
||||
|
||||
def get_taxref(self, key):
|
||||
if key in self.taxref:
|
||||
return self.taxref[key]
|
||||
return ''
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.country} {self.brand} {self.currency} "
|
||||
|
||||
|
||||
class BaseReceipt():
|
||||
"""
|
||||
will hold the receipt identification
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, store, year, month, day, till, seq):
|
||||
self.store = store
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
self.till = till
|
||||
self.seq = seq
|
||||
self.time_str = ''
|
||||
|
||||
|
||||
|
||||
def id(self):
|
||||
return f"{self.store}{self.year}{self.month}{self.day}{self.till}{self.seq.zfill(3)}"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.id()}\n"
|
||||
|
||||
def add_time(self, hhmmss_with_colons):
|
||||
self.time_str = hhmmss_with_colons
|
||||
|
||||
class BaseTotals():
|
||||
"""
|
||||
will hold the finiancial totals
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, grand, net, tax):
|
||||
self.grandAmount = float(grand)
|
||||
self.netAmount = float(net)
|
||||
self.taxAmount = float(tax)
|
||||
self.totalLinesAmount = self.netAmount
|
||||
self.roundingAmount = 0.0
|
||||
self.parent = None
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def currency(self):
|
||||
return self.parent.currency()
|
||||
|
||||
def get_total_lines_netsalesprice(self):
|
||||
self.totalLinesAmount = self.parent.get_total_lines_netsalesprice()
|
||||
# print(f" ---- {self.parent.country()} {self.parent.roundingAllowed()} {type(self.parent.roundingAllowed())}")
|
||||
if self.parent.roundingAllowed():
|
||||
self.roundingAmount = self.netAmount - self.totalLinesAmount
|
||||
else:
|
||||
roundingamount = self.netAmount - self.totalLinesAmount
|
||||
if abs(roundingamount) > 0.001:
|
||||
self.parent.fuck_prices_of_first_item_to_avoid_rounding(roundingamount)
|
||||
self.totalLinesAmount += roundingamount
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.grandAmount} {self.netAmount}{self.taxAmount}\n"
|
||||
|
||||
class BaseOrganization():
|
||||
'''
|
||||
will hold the organizations detail, can be buyer or seller(PVH)
|
||||
'''
|
||||
def __init__(self, orgid, name, city, name2='', street1='', street2='', zip='', country='EU', taxid='0', scheme=DEFAULTCUSTSCHEME, phone='', email='', legalid='', vrbl_receiver='BASE_VRBL', minimum=0.0, orgidlength=0, taxidlength=0, roundingAllowed=1):
|
||||
self.orgid = orgid
|
||||
self.scheme = scheme
|
||||
self.name = name
|
||||
self.name2 = name2
|
||||
self.street1 = street1
|
||||
self.street2 = street2
|
||||
self.zip = zip
|
||||
self.city = city
|
||||
self.country = country
|
||||
self.taxID = taxid
|
||||
self.legalid = legalid
|
||||
self.phone = phone
|
||||
self.email = email
|
||||
self.vrbl_receiver = vrbl_receiver
|
||||
self.source = ''
|
||||
self.minimum = float(minimum)
|
||||
self.orgidlength = int(orgidlength)
|
||||
self.taxidlength = int(taxidlength)
|
||||
self.roundingAllowed = int(roundingAllowed) # 0: distribute rounding diffs over the items
|
||||
self.parent = None
|
||||
|
||||
def set_source(self, source):
|
||||
self.source = source
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def __str__(self):
|
||||
return f" {self.orgid} {self.name} {self.city} {self.taxID}\n"
|
||||
|
||||
class BaseBuyer(BaseOrganization):
|
||||
def update(self, buyerdata):
|
||||
self.orgid = buyerdata['OrgId']
|
||||
self.name = buyerdata['Organization']
|
||||
self.zip = buyerdata['Zip']
|
||||
self.city = buyerdata['City']
|
||||
self.country = buyerdata['Country']
|
||||
self.taxID = buyerdata['TaxId']
|
||||
self.street1 = buyerdata['Street']
|
||||
|
||||
def _country(self):
|
||||
return self.country if len(self.country)>1 else self.parent.country()
|
||||
|
||||
def shorttxt(self):
|
||||
return f"{self.orgid}, {self.name}, {self.zip}, {self.country}, {self.taxID}, {self.email}, {self._country()}"
|
||||
|
||||
def repair_and_check_if_data_is_broken(self):
|
||||
orgid_as_number = re.sub(r"\D", "", self.orgid)
|
||||
if len(orgid_as_number)<=2 and len(self.orgid)>=6 and len(self.name)<=1:
|
||||
self.name = self.orgid
|
||||
self.orgid = ''
|
||||
else:
|
||||
self.orgid = orgid_as_number
|
||||
self.taxID = re.sub(r"\D", "", self.taxID)
|
||||
if self.orgid == '0' or self.orgid== '1' or self.orgid == '':
|
||||
self.orgid = self.taxID
|
||||
if len(self.country)==0:
|
||||
self.country = self._country()
|
||||
# print(f"{len(self.name)<2} or {len(self.city)<2} or {len(self.zip)<2} or {(self.country not in EU_COUNTRIES)} or {len(self.orgid)!=self.parent.seller.orgidlength} or {len(self.taxID)!=self.parent.seller.taxidlength}")
|
||||
return (len(self.name)<2 or len(self.city)<2 or len(self.zip)<2 or (self.country not in EU_COUNTRIES) or len(self.orgid)!=self.parent.seller.orgidlength or len(self.taxID)!=self.parent.seller.taxidlength)
|
||||
|
||||
class BaseSeller(BaseOrganization):
|
||||
pass
|
||||
|
||||
class BaseTender():
|
||||
'''
|
||||
payment-line
|
||||
'''
|
||||
def __init__(self, description, amount, type, code, currency=None):
|
||||
self.description = description
|
||||
self.type = type
|
||||
self.code = code
|
||||
self.amount = float(amount)
|
||||
self.my_currency = currency
|
||||
self.parent = None
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def translated_type(self):
|
||||
return vrbl.translate(self.type, type='tender', country=self.country())
|
||||
|
||||
def country(self):
|
||||
return self.parent.country()
|
||||
|
||||
def __str__(self):
|
||||
return(f" {self.description} {self.amount} {self.translated_type} {self.code} \n")
|
||||
|
||||
class BaseArticle():
|
||||
'''
|
||||
article info
|
||||
'''
|
||||
|
||||
def __init__(self, sequence, returnflag, itemid, upc, description, quantity, unit, basePrice, salesPrice):
|
||||
self.sequence = sequence
|
||||
self.returnflag = returnflag
|
||||
self.itemid = itemid
|
||||
self.upc = upc
|
||||
self.description = description
|
||||
self.quantity = quantity
|
||||
self.unit = unit
|
||||
self.basePrice = float(basePrice) # incl tax, before discounts
|
||||
self.salesPrice = float(salesPrice) # incl tax, after discounts
|
||||
self.discounts = []
|
||||
self.tax = None
|
||||
self.parent = None
|
||||
self.priceIncreaseForRounding = 0.0
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def add_discount(self, discount):
|
||||
discount.set_parent(self)
|
||||
self.discounts.append(discount)
|
||||
|
||||
def add_tax(self, tax):
|
||||
tax.set_parent(self)
|
||||
self.tax = tax
|
||||
|
||||
def currency(self):
|
||||
return self.parent.currency()
|
||||
|
||||
def country(self):
|
||||
return self.parent.country()
|
||||
|
||||
def taxpercentage(self):
|
||||
return self.tax.percentage
|
||||
|
||||
def netBasePrice(self):
|
||||
return before_tax(self.basePrice, self.tax.percentage) + self.priceIncreaseForRounding
|
||||
|
||||
def netSalesPrice(self):
|
||||
return before_tax(self.salesPrice, self.tax.percentage) + self.priceIncreaseForRounding
|
||||
|
||||
|
||||
def line_uuid(self):
|
||||
return f"{self.parent.receipt_id()}{self.sequence.zfill(5)}"
|
||||
|
||||
def increasePrices(self, roundingamount):
|
||||
if self.netSalesPrice() - self.priceIncreaseForRounding - (roundingamount / 100) > 0.0:
|
||||
self.priceIncreaseForRounding += (roundingamount / 100)
|
||||
return roundingamount
|
||||
return 0
|
||||
|
||||
def __str__(self):
|
||||
result = f" {self.itemid} {self.description} {self.quantity} {self.unit} {self.salesPrice} [{self.basePrice}]\n"
|
||||
for d in self.discounts:
|
||||
result += str(d)
|
||||
for t in self.taxes:
|
||||
result += str(t)
|
||||
return result
|
||||
Reference in New Issue
Block a user