niwayandm commited on
Commit ·
45e05b6
1
Parent(s): d415fe9
Add recurring charges on billing and company associations to invoice
Browse files- python/hubspot_billing.py +5 -1
- python/hubspot_invoices.py +28 -6
python/hubspot_billing.py
CHANGED
|
@@ -81,7 +81,9 @@ BILLING_PROPERTIES = [
|
|
| 81 |
"hs_created_by_user_id",
|
| 82 |
"hs_createdate",
|
| 83 |
"connection_date",
|
| 84 |
-
"disconnect_date"
|
|
|
|
|
|
|
| 85 |
]
|
| 86 |
|
| 87 |
PROPERTY_RENAME = {
|
|
@@ -289,6 +291,8 @@ def read_billing_by_ids(
|
|
| 289 |
"hubspot_created_at": parse_ts(p.get("hs_createdate")),
|
| 290 |
"connection_date": parse_ts(p.get("connection_date")),
|
| 291 |
"disconnect_date": parse_ts(p.get("disconnect_date")),
|
|
|
|
|
|
|
| 292 |
}
|
| 293 |
services.append(row)
|
| 294 |
|
|
|
|
| 81 |
"hs_created_by_user_id",
|
| 82 |
"hs_createdate",
|
| 83 |
"connection_date",
|
| 84 |
+
"disconnect_date",
|
| 85 |
+
"recurring_charges__value_",
|
| 86 |
+
"recurring_charges__daily_",
|
| 87 |
]
|
| 88 |
|
| 89 |
PROPERTY_RENAME = {
|
|
|
|
| 291 |
"hubspot_created_at": parse_ts(p.get("hs_createdate")),
|
| 292 |
"connection_date": parse_ts(p.get("connection_date")),
|
| 293 |
"disconnect_date": parse_ts(p.get("disconnect_date")),
|
| 294 |
+
"recurring_charges_value": p.get("recurring_charges__value"),
|
| 295 |
+
"recurring_charges_daily": p.get("recurring_charges__daily"),
|
| 296 |
}
|
| 297 |
services.append(row)
|
| 298 |
|
python/hubspot_invoices.py
CHANGED
|
@@ -203,7 +203,7 @@ def search_invoice_ids_after_ms(since_ms: int) -> Tuple[List[str], str]:
|
|
| 203 |
def read_invoice_by_ids(
|
| 204 |
invoice_ids: List[str],
|
| 205 |
cursor_prop: str,
|
| 206 |
-
) -> Tuple[List[Dict], List[Dict], Optional[int]]:
|
| 207 |
"""
|
| 208 |
Read invoices by ID including deal associations.
|
| 209 |
Returns: (all_invoices, max_ts_ms_for_cursor_prop)
|
|
@@ -213,8 +213,9 @@ def read_invoice_by_ids(
|
|
| 213 |
|
| 214 |
all_invoices: List[Dict] = []
|
| 215 |
invoice_contact_links: List[Dict] = []
|
|
|
|
| 216 |
|
| 217 |
-
assoc_types = ["contacts"]
|
| 218 |
|
| 219 |
max_ts_ms: Optional[int] = None
|
| 220 |
|
|
@@ -244,6 +245,17 @@ def read_invoice_by_ids(
|
|
| 244 |
"contact_id": try_parse_int(a.id),
|
| 245 |
})
|
| 246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
# Created date: accept either createdate or hs_createdate
|
| 248 |
created_iso = p.get("createdate") or p.get("hs_createdate")
|
| 249 |
|
|
@@ -273,12 +285,12 @@ def read_invoice_by_ids(
|
|
| 273 |
except (ObjectsApiException, httpx.HTTPError) as e:
|
| 274 |
logging.error("Error reading invoice %s: %s", did, e)
|
| 275 |
|
| 276 |
-
return all_invoices, invoice_contact_links, max_ts_ms
|
| 277 |
|
| 278 |
# -----------------------------------------------------------------------------
|
| 279 |
# Upsert
|
| 280 |
# -----------------------------------------------------------------------------
|
| 281 |
-
def upsert_invoices(invoices: List[Dict], invoice_contact_links: List[Dict]) -> None:
|
| 282 |
if invoices:
|
| 283 |
insert_into_supabase_table(supabase_client, "hubspot_invoices", invoices)
|
| 284 |
print(f"Upserted {len(invoices)} invoices.")
|
|
@@ -292,6 +304,16 @@ def upsert_invoices(invoices: List[Dict], invoice_contact_links: List[Dict]) ->
|
|
| 292 |
on_conflict=["invoice_id", "contact_id"],
|
| 293 |
)
|
| 294 |
print(f"Upserted {len(invoice_contact_links)} invoice-contact associations.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
|
| 296 |
|
| 297 |
# -----------------------------------------------------------------------------
|
|
@@ -329,10 +351,10 @@ def main(since_ms: Optional[int] = None):
|
|
| 329 |
return
|
| 330 |
|
| 331 |
print("Reading invoices (with associations)...")
|
| 332 |
-
invoices, invoice_contact_links, max_ts_ms = read_invoice_by_ids(ids, cursor_prop)
|
| 333 |
|
| 334 |
print("Upserting into Supabase...")
|
| 335 |
-
upsert_invoices(invoices, invoice_contact_links)
|
| 336 |
|
| 337 |
# Advance cursor to max timestamp we actually ingested for the chosen property
|
| 338 |
new_cursor_ms = max_ts_ms if max_ts_ms is not None else since_ms
|
|
|
|
| 203 |
def read_invoice_by_ids(
|
| 204 |
invoice_ids: List[str],
|
| 205 |
cursor_prop: str,
|
| 206 |
+
) -> Tuple[List[Dict], List[Dict], List[Dict], Optional[int]]:
|
| 207 |
"""
|
| 208 |
Read invoices by ID including deal associations.
|
| 209 |
Returns: (all_invoices, max_ts_ms_for_cursor_prop)
|
|
|
|
| 213 |
|
| 214 |
all_invoices: List[Dict] = []
|
| 215 |
invoice_contact_links: List[Dict] = []
|
| 216 |
+
invoice_company_links: List[Dict] = []
|
| 217 |
|
| 218 |
+
assoc_types = ["contacts", "companies"]
|
| 219 |
|
| 220 |
max_ts_ms: Optional[int] = None
|
| 221 |
|
|
|
|
| 245 |
"contact_id": try_parse_int(a.id),
|
| 246 |
})
|
| 247 |
|
| 248 |
+
# Companies
|
| 249 |
+
if getattr(record, "associations", None) and record.associations.get("companies"):
|
| 250 |
+
bucket = record.associations["companies"]
|
| 251 |
+
if getattr(bucket, "results", None):
|
| 252 |
+
for a in bucket.results:
|
| 253 |
+
if a.id and a.id.isdigit():
|
| 254 |
+
invoice_company_links.append({
|
| 255 |
+
"invoice_id": try_parse_int(record.id),
|
| 256 |
+
"company_id": try_parse_int(a.id),
|
| 257 |
+
})
|
| 258 |
+
|
| 259 |
# Created date: accept either createdate or hs_createdate
|
| 260 |
created_iso = p.get("createdate") or p.get("hs_createdate")
|
| 261 |
|
|
|
|
| 285 |
except (ObjectsApiException, httpx.HTTPError) as e:
|
| 286 |
logging.error("Error reading invoice %s: %s", did, e)
|
| 287 |
|
| 288 |
+
return all_invoices, invoice_contact_links, invoice_company_links, max_ts_ms
|
| 289 |
|
| 290 |
# -----------------------------------------------------------------------------
|
| 291 |
# Upsert
|
| 292 |
# -----------------------------------------------------------------------------
|
| 293 |
+
def upsert_invoices(invoices: List[Dict], invoice_contact_links: List[Dict], invoice_company_links: List[Dict]) -> None:
|
| 294 |
if invoices:
|
| 295 |
insert_into_supabase_table(supabase_client, "hubspot_invoices", invoices)
|
| 296 |
print(f"Upserted {len(invoices)} invoices.")
|
|
|
|
| 304 |
on_conflict=["invoice_id", "contact_id"],
|
| 305 |
)
|
| 306 |
print(f"Upserted {len(invoice_contact_links)} invoice-contact associations.")
|
| 307 |
+
|
| 308 |
+
if invoice_company_links:
|
| 309 |
+
invoice_company_links = deduplicate_by_key(invoice_company_links, key=("invoice_id", "company_id"))
|
| 310 |
+
insert_into_supabase_table(
|
| 311 |
+
supabase_client,
|
| 312 |
+
"hubspot_invoice_companies",
|
| 313 |
+
invoice_company_links,
|
| 314 |
+
on_conflict=["invoice_id", "company_id"],
|
| 315 |
+
)
|
| 316 |
+
print(f"Upserted {len(invoice_company_links)} invoice-company associations.")
|
| 317 |
|
| 318 |
|
| 319 |
# -----------------------------------------------------------------------------
|
|
|
|
| 351 |
return
|
| 352 |
|
| 353 |
print("Reading invoices (with associations)...")
|
| 354 |
+
invoices, invoice_contact_links, invoice_company_links, max_ts_ms = read_invoice_by_ids(ids, cursor_prop)
|
| 355 |
|
| 356 |
print("Upserting into Supabase...")
|
| 357 |
+
upsert_invoices(invoices, invoice_contact_links, invoice_company_links)
|
| 358 |
|
| 359 |
# Advance cursor to max timestamp we actually ingested for the chosen property
|
| 360 |
new_cursor_ms = max_ts_ms if max_ts_ms is not None else since_ms
|