Release 1.0.0

This commit is contained in:
2026-02-25 10:24:12 +01:00
parent 44fb66adfc
commit c033456bf8
35 changed files with 4891 additions and 0 deletions
+427
View File
@@ -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