# -*- coding: utf-8 -*- from psycopg2 import IntegrityError from odoo.tests.common import TransactionCase, tagged from odoo.tools.misc import mute_logger @tagged('post_install', '-at_install') class TestChargeMath(TransactionCase): def setUp(self): super().setUp() self.metric = self.env['fusion.billing.metric'].sudo().create( {'name': 'API Calls', 'code': 'api_calls', 'aggregation': 'sum'}) def _charge(self, **kw): vals = { 'name': 'Maps overage', 'plan_code': 'maps-business', 'metric_id': self.metric.id, 'charge_model': 'standard', 'included_quota': 5_000_000.0, 'price_per_unit': 0.10, 'unit_batch': 1000.0, } vals.update(kw) return self.env['fusion.billing.charge'].sudo().create(vals) def test_under_quota_is_free(self): charge = self._charge() overage_units, amount = charge._compute_billable(4_000_000.0) self.assertEqual(overage_units, 0.0) self.assertEqual(amount, 0.0) def test_standard_overage_per_1k(self): charge = self._charge() # 6,000,000 used - 5,000,000 quota = 1,000,000 overage = 1000 batches * $0.10 overage_units, amount = charge._compute_billable(6_000_000.0) self.assertEqual(overage_units, 1_000_000.0) self.assertAlmostEqual(amount, 100.0, places=2) def test_partial_batch_rounds_up(self): charge = self._charge(included_quota=0.0) # 1,500 units, batch 1000 -> 2 batches -> $0.20 _, amount = charge._compute_billable(1_500.0) self.assertAlmostEqual(amount, 0.20, places=2) def test_package_model_charges_whole_packages(self): charge = self._charge(charge_model='package', included_quota=0.0, unit_batch=1000.0, price_per_unit=2.0) # 2,001 units -> 3 packages -> $6.00 _, amount = charge._compute_billable(2_001.0) self.assertAlmostEqual(amount, 6.0, places=2) # ── item 10 (M7): only the two implemented charge models remain ── def test_charge_model_selection_limited(self): field = self.env['fusion.billing.charge']._fields['charge_model'] keys = [k for k, _label in field.selection] self.assertEqual(sorted(keys), ['package', 'standard']) self.assertNotIn('graduated', keys) self.assertNotIn('volume', keys) # ── item 11 (L1): currency is required and defaults to company currency ── def test_currency_required_and_defaulted(self): field = self.env['fusion.billing.charge']._fields['currency_id'] self.assertTrue(field.required) charge = self._charge() self.assertEqual(charge.currency_id, self.env.company.currency_id) # ── item 12 (L2): non-negative price + positive batch DB constraints ── def test_negative_price_rejected(self): with self.assertRaises(IntegrityError), mute_logger('odoo.sql_db'): with self.env.cr.savepoint(): self._charge(price_per_unit=-1.0) def test_zero_batch_rejected(self): with self.assertRaises(IntegrityError), mute_logger('odoo.sql_db'): with self.env.cr.savepoint(): self._charge(unit_batch=0.0)