diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a58708 --- /dev/null +++ b/.gitignore @@ -0,0 +1,133 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +Pipfile.lock + +# poetry +poetry.lock + +# PEP 582; used by pythonloc +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ \ No newline at end of file diff --git a/__manifest__.py b/__manifest__.py index 0e32ceb..6645c2f 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -19,7 +19,7 @@ 'version': '17.0.1.0.0', # any module necessary for this one to work correctly - 'depends': ['point_of_sale'], + 'depends': ['point_of_sale', 'pos_discount'], # always loaded 'data': [ diff --git a/models/pos_payment_method.py b/models/pos_payment_method.py index 6ca9170..fec32d2 100644 --- a/models/pos_payment_method.py +++ b/models/pos_payment_method.py @@ -11,3 +11,10 @@ class PosPaymentMethod(models.Model): domain=[('deprecated', '=', False)], help='Account used for income lines when splitting by payment method. ' 'If empty, the default income account from the product will be used.') + + discount_account_id = fields.Many2one( + 'account.account', + string='Discount Account', + domain=[('deprecated', '=', False)], + help='Account used for discount product lines when splitting by payment method. ' + 'If empty, the default discount account from the product will be used.') diff --git a/models/pos_session.py b/models/pos_session.py index f09d781..c6684bc 100644 --- a/models/pos_session.py +++ b/models/pos_session.py @@ -11,52 +11,55 @@ class PosSession(models.Model): def _accumulate_amounts(self, data): # Call the original method to get all the standard accumulations data = super(PosSession, self)._accumulate_amounts(data) - + # Get all orders in this session closed_orders = self._get_closed_orders() - + # If no orders, return original data if not closed_orders: return data - + # Get the original sales data sales = data.get('sales', {}) if not sales: return data - + # Create new sales data structure split by payment method split_sales = defaultdict(lambda: {'amount': 0.0, 'amount_converted': 0.0, 'tax_amount': 0.0}) - + + # Get discount product ID from config + discount_product_id = self.config_id.discount_product_id.id if self.config_id.discount_product_id else None + # For each sale entry, we need to distribute it across payment methods for sale_key, sale_amounts in sales.items(): # Skip if this is a tax key (we only want to split actual sales) if len(sale_key) < 4: # Not a standard sales key continue - + total_amount = sale_amounts['amount'] total_amount_converted = sale_amounts['amount_converted'] tax_amount = sale_amounts.get('tax_amount', 0.0) # Get tax amount if it exists - + if float_is_zero(total_amount, precision_rounding=self.currency_id.rounding): continue - + # Distribute this sales amount across all orders based on their payment methods total_payment_amount = sum(sum(payment.amount for payment in order.payment_ids) for order in closed_orders) - + if float_is_zero(total_payment_amount, precision_rounding=self.currency_id.rounding): continue - + # Distribute the sales amount across all orders proportionally to their payments for order in closed_orders: if order.is_invoiced: continue # Skip invoiced orders - + order_payments = order.payment_ids order_payment_total = sum(payment.amount for payment in order_payments) - + if float_is_zero(order_payment_total, precision_rounding=order.currency_id.rounding): continue - + # For each payment in this order, create a split sales entry for payment in order_payments: # Calculate the proportion of this payment relative to all payments @@ -64,19 +67,31 @@ class PosSession(models.Model): payment_amount = total_amount * payment_proportion payment_amount_converted = total_amount_converted * payment_proportion payment_tax_amount = tax_amount * payment_proportion - + if float_is_zero(payment_amount, precision_rounding=self.currency_id.rounding): continue - - # Use the payment method's income account if specified, otherwise use original account + + # Determine the account to use based on whether this is a discount product income_account_id = sale_key[0] # default account - if payment.payment_method_id.income_account_id and payment.payment_method_id.income_account_id.id: - income_account_id = payment.payment_method_id.income_account_id.id - + + # Check if this sale key corresponds to a discount product + # We need to check if the account in the sale_key matches the discount product account + if discount_product_id: + # Get the discount product's income account + discount_product = self.env['product.product'].browse(discount_product_id) + discount_account = discount_product._get_product_accounts()['income'] + if discount_account and discount_account.id == sale_key[0]: + # This is a discount product, use discount account if configured + if payment.payment_method_id.discount_account_id: + income_account_id = payment.payment_method_id.discount_account_id.id + elif payment.payment_method_id.income_account_id: + # This is a regular product, use income account if configured + income_account_id = payment.payment_method_id.income_account_id.id + # Ensure we have a valid account ID if not income_account_id: continue - + # Create a new key that includes the payment method new_sale_key = ( # account (use payment method account if specified) @@ -90,12 +105,12 @@ class PosSession(models.Model): # base tags (same as original) sale_key[3], ) - + # Update the split sales data split_sales[new_sale_key]['amount'] += payment_amount split_sales[new_sale_key]['amount_converted'] += payment_amount_converted split_sales[new_sale_key]['tax_amount'] += payment_tax_amount - + # Replace the original sales data with our split sales data data['sales'] = split_sales return data diff --git a/views/pos_payment_method_views.xml b/views/pos_payment_method_views.xml index 37c772b..233e18d 100644 --- a/views/pos_payment_method_views.xml +++ b/views/pos_payment_method_views.xml @@ -7,6 +7,7 @@ +