Source code for shuup.core.taxing.utils

# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.

from collections import defaultdict

from shuup.core.pricing import TaxfulPrice, TaxlessPrice
from shuup.utils.money import Money

from ._line_tax import SourceLineTax
from ._price import TaxedPrice


[docs]def get_tax_class_proportions(lines): """ Generate tax class proportions from taxed lines. Sum prices per tax class and return a list of (tax_class, factor) pairs, where factor is the proportion of total price of lines with given tax class from the total price of all lines. :type lines: list[shuup.core.order_creator.SourceLine] :param lines: List of taxed lines to generate proportions from :rtype: list[(shuup.core.models.TaxClass, decimal.Decimal)] :return: List of tax classes with a proportion, or empty list if total price is zero. Sum of proportions is 1. """ if not lines: return [] zero = lines[0].price.new(0) total_by_tax_class = defaultdict(lambda: zero) total = zero for line in lines: total_by_tax_class[line.tax_class] += line.price total += line.price if not total: # Can't calculate proportions, if total is zero return [] return [(tax_class, tax_class_total / total) for (tax_class, tax_class_total) in total_by_tax_class.items()]
[docs]def stacked_value_added_taxes(price, taxes): """ Stack added taxes on the given price without compounding. Note that this will not take compound taxation (Quebec) into account. :param price: Taxful or taxless price to calculate taxes for :type price: shuup.core.pricing.Price :param taxes: List of Tax objects :type taxes: list[shuup.core.models.Tax] :return: TaxedPrice with the calculated taxes. :rtype: TaxedPrice """ def money_sum(iterable): return sum(iterable, Money(0, price.currency)) if not taxes: return TaxedPrice(TaxfulPrice(price), TaxlessPrice(price), []) if price.includes_tax: taxful = price rate_sum = sum(tax.rate for tax in taxes if tax.rate) amount_sum = money_sum(tax.amount for tax in taxes if tax.amount) taxless = TaxlessPrice((taxful.amount - amount_sum) / (1 + rate_sum)) else: taxful = None # will be calculated below taxless = price line_taxes = [SourceLineTax.from_tax(tax=tax, base_amount=taxless.amount) for tax in taxes] if taxful is None: total_tax_amount = money_sum(x.amount for x in line_taxes) taxful = TaxfulPrice(taxless.amount + total_tax_amount) return TaxedPrice(taxful, taxless, line_taxes)
[docs]def calculate_compounded_added_taxes(price, tax_groups): """ Calculate compounded and added taxes from given groups of taxes. The `tax_groups` argument should be a list of tax groups, where each tax group is a list of `Tax` objects. Taxes in each tax group will be added together and finally each added tax group will be compounded over each other. :param price: Taxful or taxless price to calculate taxes for :type price: shuup.core.pricing.Price :param tax_groups: List of tax groups, each being a list of taxes :type tax_groups: list[list[shuup.core.models.Tax]] :return: TaxedPrice with the calculated taxes. :rtype: TaxedPrice """ if price.includes_tax: return _calc_compounded_added_taxes_from_taxful(price, tax_groups) else: return _calc_compounded_added_taxes_from_taxless(price, tax_groups)
def _calc_compounded_added_taxes_from_taxful(amount, tax_groups): base_price = TaxfulPrice(amount) reversed_line_taxes = [] for taxes in reversed(tax_groups): taxed_price = stacked_value_added_taxes(base_price, taxes) base_price = TaxfulPrice(taxed_price.taxless) reversed_line_taxes.extend(reversed(taxed_price.taxes)) line_taxes = list(reversed(reversed_line_taxes)) return TaxedPrice(taxful=TaxfulPrice(amount), taxless=TaxlessPrice(base_price), taxes=line_taxes) def _calc_compounded_added_taxes_from_taxless(amount, tax_groups): base_price = TaxlessPrice(amount) line_taxes = [] for taxes in tax_groups: taxed_price = stacked_value_added_taxes(base_price, taxes) base_price = TaxlessPrice(taxed_price.taxful) line_taxes.extend(taxed_price.taxes) return TaxedPrice(taxful=TaxfulPrice(base_price), taxless=TaxlessPrice(amount), taxes=line_taxes)