Skip to content

Commit a40928e

Browse files
committed
[16.0][ADD] billcom_integration
1 parent 39100d5 commit a40928e

80 files changed

Lines changed: 28996 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# Copyright 2025 Binhex - Simple Solutions
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
import logging
5+
from unittest.mock import patch
6+
7+
from odoo import fields
8+
from odoo.exceptions import UserError
9+
from odoo.tests import tagged
10+
11+
from .common import BillcomTestCommon
12+
13+
_logger = logging.getLogger(__name__)
14+
15+
16+
@tagged("post_install", "-at_install", "billcom")
17+
class TestAccountPayment(BillcomTestCommon):
18+
"""Test account.payment Bill.com integration"""
19+
20+
@classmethod
21+
def setUpClass(cls):
22+
super().setUpClass()
23+
24+
# Create a payment for testing
25+
cls.payment = cls.env["account.payment"].create(
26+
{
27+
"payment_type": "outbound",
28+
"partner_type": "supplier",
29+
"partner_id": cls.vendor_billcom.id,
30+
"amount": 100.0,
31+
"journal_id": cls.bank_journal.id,
32+
"date": fields.Date.today(),
33+
}
34+
)
35+
36+
def test_payment_billcom_fields(self):
37+
"""Test that payments have all required Bill.com fields"""
38+
required_fields = [
39+
"billcom",
40+
"billcom_id",
41+
"is_sync_to_billcom",
42+
"billcom_sync_status",
43+
]
44+
45+
for field in required_fields:
46+
self.assertTrue(
47+
hasattr(self.payment, field), f"Payment should have field: {field}"
48+
)
49+
50+
@patch(
51+
"odoo.addons.billcom_integration.models.billcom_service.BillcomService._make_request"
52+
)
53+
def test_sync_payment_to_billcom(self, mock_request):
54+
"""Test syncing payment to Bill.com"""
55+
mock_request.return_value = {
56+
"id": "payment_abc123",
57+
"amount": 100.0,
58+
"processDate": fields.Date.today().isoformat(),
59+
"singleStatus": "SCHEDULED",
60+
}
61+
62+
# Trigger sync
63+
self.payment.sync_to_billcom()
64+
65+
# Verify billcom_id was updated
66+
self.assertEqual(self.payment.billcom_id, "payment_abc123")
67+
68+
@patch(
69+
"odoo.addons.billcom_integration.models.billcom_service.BillcomService._make_request"
70+
)
71+
def test_payment_status_update_from_webhook(self, mock_request):
72+
"""Test payment status update from Bill.com webhook"""
73+
self.payment.billcom_id = "payment_webhook_001"
74+
75+
mock_request.return_value = {
76+
"id": "payment_webhook_001",
77+
"singleStatus": "PAID",
78+
"paidDate": fields.Date.today().isoformat(),
79+
}
80+
81+
# Sync from Bill.com
82+
self.payment.sync_from_billcom_by_id("payment_webhook_001")
83+
84+
# Verify payment was synced
85+
self.assertEqual(self.payment.billcom_id, "payment_webhook_001")
86+
87+
def test_payment_for_vendor_bill(self):
88+
"""Test creating payment for vendor bill"""
89+
# Post the vendor bill
90+
self.vendor_bill.action_post()
91+
92+
# Create payment from bill
93+
payment = (
94+
self.env["account.payment"]
95+
.with_context(active_ids=[self.vendor_bill.id], active_model="account.move")
96+
.create(
97+
{
98+
"payment_type": "outbound",
99+
"partner_type": "supplier",
100+
"partner_id": self.vendor_billcom.id,
101+
"amount": self.vendor_bill.amount_total,
102+
"journal_id": self.bank_journal.id,
103+
}
104+
)
105+
)
106+
107+
self.assertEqual(payment.partner_id, self.vendor_billcom)
108+
self.assertEqual(payment.amount, self.vendor_bill.amount_total)
109+
110+
def test_payment_multi_currency(self):
111+
"""Test payment in different currency"""
112+
eur = self.env["res.currency"].search([("name", "=", "EUR")], limit=1)
113+
if not eur:
114+
eur = self.env["res.currency"].create({"name": "EUR", "symbol": "€"})
115+
116+
payment = self.env["account.payment"].create(
117+
{
118+
"payment_type": "outbound",
119+
"partner_type": "supplier",
120+
"partner_id": self.vendor_billcom.id,
121+
"amount": 100.0,
122+
"currency_id": eur.id,
123+
"journal_id": self.bank_journal.id,
124+
}
125+
)
126+
127+
self.assertEqual(payment.currency_id, eur)
128+
129+
def test_payment_funding_account(self):
130+
"""Test payment with Bill.com funding account"""
131+
# Create funding account
132+
funding_account = self.env["billcom.funding.account"].create(
133+
{
134+
"name": "Test Bank Account",
135+
"billcom_id": "funding_001",
136+
"account_type": "Checking",
137+
"last_four_digits": "1234",
138+
}
139+
)
140+
141+
# Payment with funding account (if field exists)
142+
if hasattr(self.payment, "billcom_funding_account_id"):
143+
self.payment.billcom_funding_account_id = funding_account
144+
self.assertEqual(self.payment.billcom_funding_account_id, funding_account)
145+
146+
@patch(
147+
"odoo.addons.billcom_integration.models.billcom_service.BillcomService._make_request"
148+
)
149+
def test_payment_sync_error_handling(self, mock_request):
150+
"""Test error handling during payment sync"""
151+
mock_request.side_effect = UserError("Payment sync failed")
152+
153+
with self.assertRaises(UserError):
154+
self.payment.sync_to_billcom()
155+
156+
def test_payment_date_validation(self):
157+
"""Test payment date is required"""
158+
payment = self.env["account.payment"].create(
159+
{
160+
"payment_type": "outbound",
161+
"partner_type": "supplier",
162+
"partner_id": self.vendor_billcom.id,
163+
"amount": 100.0,
164+
"journal_id": self.bank_journal.id,
165+
"date": fields.Date.today(),
166+
}
167+
)
168+
169+
self.assertTrue(payment.date)
170+
171+
def test_customer_payment_not_synced_to_billcom(self):
172+
"""Test customer payments (inbound) sync behavior"""
173+
customer_payment = self.env["account.payment"].create(
174+
{
175+
"payment_type": "inbound",
176+
"partner_type": "customer",
177+
"partner_id": self.customer_billcom.id,
178+
"amount": 100.0,
179+
"journal_id": self.bank_journal.id,
180+
}
181+
)
182+
183+
self.assertEqual(customer_payment.payment_type, "inbound")
184+
185+
@patch(
186+
"odoo.addons.billcom_integration.models.billcom_service_abstract.BillcomServiceAbstract._make_request" # noqa B950
187+
)
188+
def test_update_billcom_payment_status(self, mock_request):
189+
"""Test updating payment status from Bill.com"""
190+
# Setup a payment that needs update
191+
self.payment.billcom_id = "payment_to_update"
192+
self.payment.billcom_payment_status = "scheduled"
193+
self.payment.last_sync_date = fields.Datetime.now() - fields.timedelta(hours=2)
194+
195+
# Mock response
196+
mock_request.return_value = {
197+
"id": "payment_to_update",
198+
"singleStatus": "PROCESSED",
199+
"confirmationNumber": "CONF123",
200+
}
201+
202+
# Run update
203+
self.env["account.payment"].update_billcom_payment_status()
204+
205+
# Verify status updated
206+
self.assertEqual(self.payment.billcom_payment_status, "processed")
207+
self.assertEqual(self.payment.billcom_confirmation_number, "CONF123")
208+
209+
@patch(
210+
"odoo.addons.billcom_integration.models.billcom_service_abstract.BillcomServiceAbstract._make_request" # noqa B950
211+
)
212+
def test_action_cancel_billcom_payment(self, mock_request):
213+
"""Test canceling payment in Bill.com"""
214+
self.payment.billcom_id = "payment_to_cancel"
215+
self.payment.billcom_payment_status = "scheduled"
216+
217+
mock_request.return_value = {"id": "payment_to_cancel", "status": "CANCELED"}
218+
219+
self.payment.action_cancel_billcom_payment()
220+
221+
self.assertEqual(self.payment.billcom_payment_status, "canceled")
222+
self.assertEqual(self.payment.state, "cancel")
223+
224+
@patch(
225+
"odoo.addons.billcom_integration.models.billcom_service_abstract.BillcomServiceAbstract._make_request" # noqa B950
226+
)
227+
def test_action_get_payment_status(self, mock_request):
228+
"""Test manual status fetch"""
229+
self.payment.billcom_id = "payment_status_check"
230+
231+
mock_request.return_value = {
232+
"id": "payment_status_check",
233+
"singleStatus": "SENT",
234+
}
235+
236+
self.payment.action_get_payment_status()
237+
238+
self.assertEqual(self.payment.billcom_payment_status, "sent")
239+
240+
def test_prepare_payment_data_wallet(self):
241+
"""Test payment data preparation for WALLET funding type"""
242+
self.payment.billcom_funding_account_type = "WALLET"
243+
# Wallet requires process date
244+
self.payment.billcom_process_date = fields.Date.today() + fields.timedelta(
245+
days=1
246+
)
247+
248+
data = self.payment._prepare_payment_data()
249+
250+
self.assertEqual(data["fundingAccount"]["type"], "WALLET")
251+
self.assertIn("processDate", data)

0 commit comments

Comments
 (0)