Skip to content

Commit 174b360

Browse files
Merge pull request #46 from softwareone-platform/MPT-16794-ext-fix-date-bug
[MPT-16794] - Fix bug in calculating previous year.
2 parents 55230c2 + 5b86ecb commit 174b360

File tree

3 files changed

+132
-43
lines changed

3 files changed

+132
-43
lines changed

ffc/billing/process_billing.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ async def process_billing(
7878
"""
7979
product_id = settings.MPT_PRODUCTS_IDS[0]
8080
mpt_client = MPTAsyncClient()
81-
8281
if authorization_id:
8382
authorization = await mpt_client.fetch_authorization(authorization_id)
8483
processor = AuthorizationProcessor(year, month, authorization, dry_run)

ffc/management/commands/process_billing.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,48 +11,64 @@
1111

1212

1313
class Command(BaseCommand):
14-
help = "Synchronize agreements on anniversary."
14+
help = "Generate billing files for a given period."
1515

1616
def add_arguments(self, parser): # pragma no cover
17+
previous_month = date.today() - relativedelta(months=1)
1718
parser.add_argument(
1819
"--dry-run",
1920
action="store_true",
2021
default=False,
21-
help="Test generation of billing files without making changes",
22+
help="Test generation of billing files without making changes.",
2223
)
2324

2425
parser.add_argument(
2526
"--authorization",
26-
help="Generate billing file for given authorization",
27+
help="Generate billing file for given authorization.",
2728
)
2829
parser.add_argument(
2930
"--year",
3031
type=int,
31-
default=date.today().year,
32-
help="Year for billing period",
32+
default=previous_month.year,
33+
help=f"Year for billing period. Default to {previous_month.year}",
3334
)
3435
parser.add_argument(
3536
"--month",
3637
type=int,
37-
default=(date.today() - relativedelta(months=1)).month,
38-
help="Year for billing period",
38+
default=previous_month.month,
39+
help=f"Month for billing period. Default to {previous_month.month}.",
3940
)
4041
parser.add_argument(
4142
"--cutoff-day",
4243
type=int,
4344
default=5,
44-
help="The cutoff day to run the process for.",
45+
help="The cutoff day to run the process for. Default is the 5th",
4546
)
4647

4748
def handle(self, *args, **options):
4849
cutoff_day = options["cutoff_day"]
49-
if cutoff_day not in range(1, 29):
50-
self.stderr.write(self.style.ERROR("cutoff-day must be between 1 and 28 (inclusive)"))
50+
billing_month = options["month"]
51+
billing_year = options["year"]
52+
today = date.today()
53+
54+
if not (1 <= cutoff_day <= 28):
55+
self.stderr.write(
56+
self.style.ERROR("The cutoff-day must be between 1 and 28 (inclusive)")
57+
)
58+
sys.exit(1)
59+
60+
if not (1 <= billing_month <= 12):
61+
self.stderr.write(
62+
self.style.ERROR("The billing month must be between 1 and 12 (inclusive)")
63+
)
64+
sys.exit(1)
65+
if (billing_year, billing_month) > (today.year, today.month):
66+
self.stderr.write(self.style.ERROR("The billing period cannot be in the future"))
5167
sys.exit(1)
5268
asyncio.run(
5369
process_billing(
54-
options["year"],
55-
options["month"],
70+
billing_year,
71+
billing_month,
5672
authorization_id=options.get("authorization"),
5773
dry_run=options["dry_run"],
5874
cutoff_day=cutoff_day,

tests/ffc/test_process_billing.py

Lines changed: 104 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,41 +1441,115 @@ def test_get_trial_days_full_month(billing_process_instance):
14411441
# # -----------------------------------------------------------------------------------
14421442
# # - Test command
14431443
@pytest.mark.parametrize(
1444-
"opts",
1444+
("opts", "not_allowed", "error_message"),
14451445
[
1446-
{
1447-
"year": 2025,
1448-
"month": 8,
1449-
"dry_run": True,
1450-
"cutoff_day": 1,
1451-
},
1452-
{
1453-
"year": 2024,
1454-
"month": 12,
1455-
"dry_run": False,
1456-
"authorization": "AUTH123",
1457-
"cutoff_day": 1,
1458-
},
1446+
(
1447+
{"year": 2025, "month": 8, "dry_run": True, "cutoff_day": 1},
1448+
False,
1449+
None,
1450+
),
1451+
(
1452+
{
1453+
"year": 2024,
1454+
"month": 12,
1455+
"dry_run": False,
1456+
"authorization": "AUTH123",
1457+
"cutoff_day": 1,
1458+
},
1459+
False,
1460+
None,
1461+
),
1462+
(
1463+
{
1464+
"year": date.today().year + 1,
1465+
"month": 1,
1466+
"dry_run": True,
1467+
"cutoff_day": 5,
1468+
},
1469+
True,
1470+
"The billing period cannot be in the future",
1471+
),
1472+
(
1473+
{
1474+
"dry_run": True,
1475+
},
1476+
False,
1477+
None,
1478+
),
1479+
(
1480+
{
1481+
"year": date.today().year,
1482+
"dry_run": True,
1483+
"cutoff_day": 5,
1484+
},
1485+
True,
1486+
"The billing period cannot be in the future",
1487+
),
1488+
(
1489+
{
1490+
"month": max(1, date.today().month - 1),
1491+
"dry_run": True,
1492+
"cutoff_day": 5,
1493+
},
1494+
False,
1495+
None,
1496+
),
1497+
(
1498+
{
1499+
"month": 20,
1500+
"dry_run": True,
1501+
},
1502+
True,
1503+
"The billing month must be between 1 and 12 (inclusive)",
1504+
),
1505+
(
1506+
{
1507+
"cutoff_day": 100,
1508+
"dry_run": True,
1509+
},
1510+
True,
1511+
"The cutoff-day must be between 1 and 28 (inclusive)",
1512+
),
14591513
],
14601514
)
1461-
def test_handle_run_command(mocker, opts):
1462-
fake_coro_obj = object()
1463-
process_billing_mock = Mock(return_value=fake_coro_obj)
1464-
asyncio_run_mock = Mock()
1465-
1466-
mocker.patch("ffc.management.commands.process_billing.process_billing", process_billing_mock)
1467-
mocker.patch("ffc.management.commands.process_billing.asyncio.run", asyncio_run_mock)
1468-
call_command("process_billing", **opts)
1469-
expected_auth = opts.get("authorization")
1470-
process_billing_mock.assert_called_once_with(
1471-
opts["year"],
1472-
opts["month"],
1473-
authorization_id=expected_auth,
1474-
dry_run=opts["dry_run"],
1475-
cutoff_day=opts["cutoff_day"],
1515+
def test_process_billing_command(mocker, opts, not_allowed, error_message, capsys):
1516+
fake_coro = object()
1517+
mock_process_billing = Mock(return_value=fake_coro)
1518+
mock_asyncio_run = Mock()
1519+
1520+
mocker.patch(
1521+
"ffc.management.commands.process_billing.process_billing",
1522+
mock_process_billing,
1523+
)
1524+
mocker.patch(
1525+
"ffc.management.commands.process_billing.asyncio.run",
1526+
mock_asyncio_run,
14761527
)
14771528

1478-
asyncio_run_mock.assert_called_once_with(fake_coro_obj)
1529+
if not_allowed:
1530+
with pytest.raises(SystemExit) as excinfo:
1531+
call_command("process_billing", **opts)
1532+
1533+
assert excinfo.value.code == 1
1534+
1535+
captured = capsys.readouterr()
1536+
assert error_message in captured.err
1537+
1538+
mock_process_billing.assert_not_called()
1539+
mock_asyncio_run.assert_not_called()
1540+
else:
1541+
call_command("process_billing", **opts)
1542+
if "year" in opts and "month" in opts:
1543+
mock_process_billing.assert_called_once_with(
1544+
opts["year"],
1545+
opts["month"],
1546+
authorization_id=opts.get("authorization"),
1547+
dry_run=opts["dry_run"],
1548+
cutoff_day=opts["cutoff_day"],
1549+
)
1550+
else:
1551+
mock_process_billing.assert_called_once()
1552+
mock_asyncio_run.assert_called_once_with(fake_coro)
14791553

14801554

14811555
# # -----------------------------------------------------------------------------------

0 commit comments

Comments
 (0)