Backend

API contract, database changes, business logic orchestration, and integration points for the Order Update backend in crelio-app.

👤 Mohammad Ashfaque Alam📅 Updated: Mar 16, 2026🏷️ feature🏷️ backend🏷️ django🏷️ finance

Backend

Repo: crelio-app Files: finance/views/bill_update.py Github Link finance/views/bill_update_helpers.py Github Link


API

POST /api-v3/finance/bill/<lab_bill_id>/update/

Authentication: Session-based. lab_id is read from the active session.

Request Body

KeyTypeRequiredDescription
billobjectCore bill fields
testslistTest objects with updated amounts/concessions
paymentListlistPayments to add or update
billIcdobjectICD code data grouped by bill ID
modifiersobjectBilling modifiers
billAddTypeinteger0 = update only; 1 = also add new tests
reportModeintegerReport print mode
aoeFormValuesobjectAOE form values for new tests
orgIdintegerOrganisation ID
missingFieldConfigUsedobjectMissing-field config IDs by category

Key bill Fields

FieldDescription
billIdInternal DB primary key
billTotalAmountUpdated total amount
billConcessionDiscount amount
billAdvanceAdvance paid
billCommentsFree-text notes
billTimeBill date + time
sampleDateSample collection date
sampleDateChangeFlag1 if sample date is intentionally being changed
sourceBilling source (cash, insurance, free, etc.)
paymentUpdateFlag1 = payment section was edited
deletedPaymentIdList of payment IDs to hard-delete
orgIdOrganisation ID
docIdReferral doctor ID
modeEmergency report flag
billLocked1 = lock the bill

Response

// Success
{ "message": "Bill details updated successfully" }

// Success with new tests added
{
  "message": "Bill details updated successfully",
  "sampleDetailsList": [ ... ]
}

// Failure
{ "message": "Failed to update bill details" }

Execution Flow


Step Reference

#MethodWhat It Does
1SnapshotRecords original field values for diff comparison
2initialize_data()Extracts and normalises the request payload
3validate()Verifies UserDetails exists
4update_bill_tests()Bulk-updates test amounts and concessions in BillingInfo
5handle_payments()Gated by paymentUpdateFlag; adds, updates, and deletes payments
6update_org()Reconciles org ledger (same-org update or org change)
7update_user_details()Adjusts patient outstanding balance
8update_doctor_revenue()Recalculates doctor cut per test
9update_appointments()Syncs EmrAppointments and HomeCollection records
10update_doctor_info()Updates referral name displayed on the bill
11update_bill_fields()Persists all core bill field updates
12update_bill_info_fields()Persists test-level BillingInfo field changes
13update_icd_data()Delete-then-save ICD codes via BillICDService
14update_modifiers()Replaces billing modifiers
15update_lab_reports()Updates LabReportRelation metadata
16update_elasticsearch()Pushes changes to ES; retried up to 2 times with 1s wait
17bill_details.save()Commits the Billing model

Payment Handling

Payment processing runs only when paymentUpdateFlag is set in the request.

Adding Payments

Each paymentList item with addFlag = true and no paymentID creates a new Payments record. If no payment list is provided, a fallback CASH payment is created from the current advance amount.

Updating Payments

Items with a paymentID update the existing record in-place (amount, type, bank, card/cheque details).

Deleting Payments

IDs in deletedPaymentId are hard-deleted (excluding refund-type payments). Deleted amounts are tracked separately for patient vs. org payments, and flow into ledger reconciliation.

Payment Conflict Guard

The update returns early with status 6 when all of these are true simultaneously:

  • All payment detail fields are populated
  • The total amount has changed
  • The org has a standard payment type (prepaid or postpaid)

[!WARNING] Status 6 means the caller must resolve the payment conflict before retrying. No DB changes are made.


Organisation & Ledger

When a bill is linked to an org, currentDue must stay in sync.

Same Org Update

  1. Calculates amount difference (old vs. new total)
  2. Updates currentDue on the org record
  3. Routes to one of three ledger cases:
    • Deleted patient payments → logs the deleted amount
    • Standard update → logs the amount difference
    • Refund scenario → calculates and logs separate patient + org refund amounts

Org Change

  • Old org: ledger is credited back (bill amount reversed)
  • New org: ledger is debited (bill amount charged)
  • currentDue adjusted on both

Org Types

CodeTypeLedger Handling
0PostpaidSALES ledger entries; currentDue tracked
1PrepaidPAYMENT ledger entries; advance-based
2OtherSkipped for most ledger operations

[!NOTE] Orgs with manageLedger = 0 bypass the Fusion ledger webhook. Their currentDue is updated directly in the database.


Doctor Revenue

When a doctor is attached, revenue is recalculated per test:

  1. Fetches ListDoctorRelation for the doctor
  2. For each test, checks ListTestRelation for a revenue amount
  3. Applies concession threshold:
    • Concession > threshold AND discardDiscountedRevFlag is set → doctor gets ₹0
    • Otherwise: doctor_amount = base_amount − (concession × sharing % / 100)
  4. If payment type is FREE → all amounts and concessions forced to 0

Audit Diff

Every bill update records a structured before/after diff in the ActivityLog.

Tracked Fields

FieldNotes
sourceBilling source
billTimeFormatted as DD Mon YYYY, HH:MM AM/PM
billAdditionalCategoryAdditional category
orderNumberOrder / reference number
billCommentsFree-text comments
billTotalAmountCompared numerically
billConcessionCompared numerically
billReferalReferral name
sampleDateOnly tracked if sampleDateChangeFlag = 1
modeEmergency report flag
orgIdShows organisation full name
testAmount per testCompared numerically
testConsc per testCompared numerically
billAdvanceCompared numerically

Diff JSON Structure

{
  "info": "Bill Information of Bill Id 428 for user John Doe (Id: 1234)",
  "update": [
    {
      "Bill Data": [
        {
          "Total Amount": { "old_value": 500.0, "new_value": 600.0 },
          "Comments":     { "old_value": "",    "new_value": "Urgent" }
        }
      ]
    },
    {
      "Test Details": [
        {
          "Test Amount for CBC": { "old_value": 200.0, "new_value": 250.0, "updateFor": "CBC" }
        }
      ]
    }
  ]
}

If no fields changed, diff is null — the ActivityLog entry is still created.


Activity Logs Written

CategoryWhenContents
17 — Bill UpdateAlwaysBill ID, patient name, user, timestamp, diff
14 — Payment AddedWhen payment total increasedOld total, new total, bill ID
15 — Payment DeletedWhen payments were deletedDeleted amount, bill ID, patient name/ID

Elasticsearch Sync

After the transaction commits, the patient_reports and userdetails indices are updated.

patient_reports Index

FieldSource
lastUpdatedCurrent timestamp
orgId.*Full org object
modeEmergency report flag
userDetailsId.totalAmountPatient outstanding balance
billId.docIdDoctor ID
billId.sourceBilling source
billId.billTimeBill timestamp
billId.billReferalDoctor name
billId.billCommentsComments
billId.billTotalAmountTotal amount
billId.orderNumberOrder number
billId.isCompleteCompletion flag
billId.branch_idBranch
billId.billServiceBill service
sampleDateOnly when sampleDateChangeFlag = 1

userdetails Index

FieldSource
totalAmountPatient outstanding (0 if addTestFlag is set)
sourceBilling source
lastUpdatedTimeCurrent timestamp
advanceFlagWhether advance is applicable
creditFlagWhether credit balance exists

Integration Webhooks

Bill Integration (Async)

After the transaction commits, a Fusion webhook notifies connected integrations:

PropertyValue
URLPY2_WEBHOOK_URL/integration/bill/trigger/
MethodPOST via Fusion task queue (task_type=2)
Payload{ lab_id, lab_bill_id, lab_user_id }

This is fire-and-forget — the response is not waited on.

Legacy Test Addition (Synchronous)

When billAddType != 0, a synchronous call is made to the legacy PY2 system:

PropertyValue
URLPY2_WEBHOOK_URL/billAddTest/
MethodPOST (form-encoded, session cookies forwarded)
ResponseIncluded in the bill update API response as sampleDetailsList

Error Handling

CheckFailure Behaviour
lab_id or lab_bill_id missingValidationError → 400
Empty payloadReturns 400 immediately
Billing record not foundSkips update; 200 with "failed" flag if billAddType != 0
UserDetails not foundException → full rollback
BillingInfo not found when updating testsException → full rollback
Lab user not found during cash-box updateException → full rollback
Missing field config errorSilently caught, reported to Sentry — does not roll back
Elasticsearch failureRetried once after 1s; failure does not roll back the transaction

On this page