Project

General

Profile

Actions

Feature #961

open

Accounting Ledger Implementation – Patient-Based Ledger (Ticket 809)

Added by RishiKesh Tuniki about 1 month ago. Updated 22 days ago.

Status:
Resolved
Priority:
Normal
Assignee:
Start date:
03/23/2026
Due date:
% Done:

100%

Estimated time:
(Total: 0:00 h)
Test Phase:
Select Test Phase

Description

We need to implement the Accounting Ledger aligned with the final ledger template defined in Ticket 809.

There are ~18 supporting files that describe the full data flow and data creation process. These are for reference only — no need to focus on the internal flow . The primary goal is to achieve the final ledger output correctly.

Objective

Generate a patient-level accounting ledger using a single PatientId , ensuring all entries are correctly derived and mapped.

Key Requirements

  • Ledger should be generated per patient (PatientId-based).

  • All ledger entries must be tied to procedures.

  • Consolidate data from the following tables:

    • PatientProcedures
    • PatientPaymentPlans
    • PatientPaymentPlanCharges
    • Patients
    • PatientProviders
    • PatientLedgerEntries

Expected Outcome

  • Final ledger output should strictly match the defined ledger template.
  • Data should be accurate, traceable, and consolidated per patient.

the files have the calculation needs to be done


Files

SIM 9 - PARTIAL PATIENT PAYMENTS (PAYSPLITS, REMAINING BALANCE).docx (23 KB) SIM 9 - PARTIAL PATIENT PAYMENTS (PAYSPLITS, REMAINING BALANCE).docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 15 - POSITIVE ADJUSTMENT (MISC CHARGE_ FINANCE CHARGE).docx (26.7 KB) SIM 15 - POSITIVE ADJUSTMENT (MISC CHARGE_ FINANCE CHARGE).docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 17 - CLINICS ENABLED SCENARIO (PAYMENTS_PAYSPLITS TIED TO A SPECIFIC CLINIC).docx (29 KB) SIM 17 - CLINICS ENABLED SCENARIO (PAYMENTS_PAYSPLITS TIED TO A SPECIFIC CLINIC).docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 16 - FAMILY SPLIT PAYMENT (ONE PAYMENT SPLIT ACROSS TWO FAMILY MEMBERS).docx (28 KB) SIM 16 - FAMILY SPLIT PAYMENT (ONE PAYMENT SPLIT ACROSS TWO FAMILY MEMBERS).docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 1 - CASH.docx (16.2 KB) SIM 1 - CASH.docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 2 - PRIMARY PAYS PARTIAL.docx (17.8 KB) SIM 2 - PRIMARY PAYS PARTIAL.docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 3 - PPO WRITE-OFF.docx (19.1 KB) SIM 3 - PPO WRITE-OFF.docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 4 - DEDUCTIBLE.docx (19.5 KB) SIM 4 - DEDUCTIBLE.docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 5 - PRIMARY & SECONDARY INSURANCE.docx (22.2 KB) SIM 5 - PRIMARY & SECONDARY INSURANCE.docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 6 - UNDERPAID_ REMAINING INSURANCE ESTIMATE.docx (82.9 KB) SIM 6 - UNDERPAID_ REMAINING INSURANCE ESTIMATE.docx RishiKesh Tuniki, 03/20/2026 12:14 PM
SIM 7 - ADJUSTMENT DISCOUNT.docx (21.8 KB) SIM 7 - ADJUSTMENT DISCOUNT.docx RishiKesh Tuniki, 03/20/2026 12:15 PM
SIM 10 - PATIENT REFUND (OVERPAYMENT, THEN REFUND).docx (24.2 KB) SIM 10 - PATIENT REFUND (OVERPAYMENT, THEN REFUND).docx RishiKesh Tuniki, 03/20/2026 12:15 PM
SIM 11 - PAYMENT PLAN (PAYPLAN + PAYPLANCHARGE + PAYMENT APPLIED TO PLAN).docx (26.9 KB) SIM 11 - PAYMENT PLAN (PAYPLAN + PAYPLANCHARGE + PAYMENT APPLIED TO PLAN).docx RishiKesh Tuniki, 03/20/2026 12:15 PM
SIM 12 - CLAIM CORRECTION_ REVERSAL (NEGATIVE SUPPLEMENTAL INSURANCE PAYMENT).docx (26.3 KB) SIM 12 - CLAIM CORRECTION_ REVERSAL (NEGATIVE SUPPLEMENTAL INSURANCE PAYMENT).docx RishiKesh Tuniki, 03/20/2026 12:15 PM
SIM 14 - HIDDEN PREPAYMENT (HIDDEN SPLITS_ _DO NOT SHOW ON ACCOUNT_).docx (27.8 KB) SIM 14 - HIDDEN PREPAYMENT (HIDDEN SPLITS_ _DO NOT SHOW ON ACCOUNT_).docx RishiKesh Tuniki, 03/20/2026 12:15 PM
SIM 8 - OVERPAYMENT_ PREPAYMENT.docx (24.5 KB) SIM 8 - OVERPAYMENT_ PREPAYMENT.docx RishiKesh Tuniki, 03/20/2026 12:15 PM
SIM 18 - CUSTOM SCENARIO.docx (31.3 KB) SIM 18 - CUSTOM SCENARIO.docx RishiKesh Tuniki, 03/20/2026 12:15 PM
Open Dental Accounting Ledger Simulation.docx (28.2 KB) Open Dental Accounting Ledger Simulation.docx RishiKesh Tuniki, 03/20/2026 12:15 PM
ACCOUNTING LEDGER - INFORMATION.docx (19.4 KB) ACCOUNTING LEDGER - INFORMATION.docx RishiKesh Tuniki, 03/20/2026 12:15 PM
dentpal_open_dental_ledger_template_deepcheck_final.xlsx (53.6 KB) dentpal_open_dental_ledger_template_deepcheck_final.xlsx RishiKesh Tuniki, 03/20/2026 12:15 PM
SIM 13 - INSURANCE OVERPAYMENT + INSURANCE REFUND.docx (26.5 KB) SIM 13 - INSURANCE OVERPAYMENT + INSURANCE REFUND.docx RishiKesh Tuniki, 03/20/2026 12:15 PM
clipboard-202603261052-8bncb.png (64 KB) clipboard-202603261052-8bncb.png RishiKesh Tuniki, 03/26/2026 01:22 AM
clipboard-202603261052-8bncb.png

Subtasks 1 (1 open0 closed)

Feature #972: Implement PaySplits & Family Insurance APIs in Windows Service for Accounting LedgerResolvedThuan L03/23/2026

Actions
Actions #3

Updated by RishiKesh Tuniki about 1 month ago

the tables structures

-- Table: public.PatientLedgerEntries

-- DROP TABLE IF EXISTS public."PatientLedgerEntries";

CREATE TABLE IF NOT EXISTS public."PatientLedgerEntries"
(
    "Id" uuid NOT NULL,
    "PatientId" uuid NOT NULL,
    "EntryDate" timestamp without time zone NOT NULL,
    "EntryType" integer NOT NULL,
    "Description" text COLLATE pg_catalog."default",
    "Amount" numeric NOT NULL,
    "Category" integer NOT NULL,
    "SourceId" text COLLATE pg_catalog."default" NOT NULL DEFAULT ''::text,
    "ExtraProperties" text COLLATE pg_catalog."default" NOT NULL,
    "ConcurrencyStamp" character varying(40) COLLATE pg_catalog."default" NOT NULL,
    "CreationTime" timestamp without time zone NOT NULL,
    "CreatorId" uuid,
    "LastModificationTime" timestamp without time zone,
    "LastModifierId" uuid,
    "TenantId" uuid,
    "ClinicId" uuid,
    "PMSSourceSystem" integer NOT NULL,
    CONSTRAINT "PK_PatientLedgerEntries" PRIMARY KEY ("Id")
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public."PatientLedgerEntries"
    OWNER to postgres;
-- Index: IX_PatientLedgerEntry_TenantId_ClinicId_SourceId_EntryType_PMSS

-- DROP INDEX IF EXISTS public."IX_PatientLedgerEntry_TenantId_ClinicId_SourceId_EntryType_PMSS";

CREATE UNIQUE INDEX IF NOT EXISTS "IX_PatientLedgerEntry_TenantId_ClinicId_SourceId_EntryType_PMSS"
    ON public."PatientLedgerEntries" USING btree
    ("TenantId" ASC NULLS LAST, "ClinicId" ASC NULLS LAST, "SourceId" COLLATE pg_catalog."default" ASC NULLS LAST, "EntryType" ASC NULLS LAST, "PMSSourceSystem" ASC NULLS LAST)
    TABLESPACE pg_default;

-- Table: public.PatientProviders

-- DROP TABLE IF EXISTS public."PatientProviders";

CREATE TABLE IF NOT EXISTS public."PatientProviders"
(
    "Id" uuid NOT NULL,
    "PMSProviderId" character varying(100) COLLATE pg_catalog."default" NOT NULL,
    "ProviderType" integer NOT NULL,
    "FirstName" character varying(100) COLLATE pg_catalog."default" NOT NULL,
    "LastName" character varying(100) COLLATE pg_catalog."default" NOT NULL,
    "Abbreviation" character varying(20) COLLATE pg_catalog."default",
    "PMSSpecialtyCode" character varying(50) COLLATE pg_catalog."default",
    "PMSSpecialtyName" character varying(100) COLLATE pg_catalog."default",
    "ExtraProperties" text COLLATE pg_catalog."default" NOT NULL,
    "ConcurrencyStamp" character varying(40) COLLATE pg_catalog."default" NOT NULL,
    "CreationTime" timestamp without time zone NOT NULL,
    "CreatorId" uuid,
    "LastModificationTime" timestamp without time zone,
    "LastModifierId" uuid,
    "TenantId" uuid,
    "ClinicId" uuid,
    "PMSSourceSystem" integer NOT NULL,
    CONSTRAINT "PK_PatientProviders" PRIMARY KEY ("Id")
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public."PatientProviders"
    OWNER to postgres;
-- Index: IX_Provider_TenantId_ClinicId_PMSProviderId_PMSSourceSystem

-- DROP INDEX IF EXISTS public."IX_Provider_TenantId_ClinicId_PMSProviderId_PMSSourceSystem";

CREATE UNIQUE INDEX IF NOT EXISTS "IX_Provider_TenantId_ClinicId_PMSProviderId_PMSSourceSystem"
    ON public."PatientProviders" USING btree
    ("TenantId" ASC NULLS LAST, "ClinicId" ASC NULLS LAST, "PMSProviderId" COLLATE pg_catalog."default" ASC NULLS LAST, "PMSSourceSystem" ASC NULLS LAST)
    TABLESPACE pg_default;

-- Table: public.Patients

-- DROP TABLE IF EXISTS public."Patients";

CREATE TABLE IF NOT EXISTS public."Patients"
(
    "Id" uuid NOT NULL,
    "TenantId" uuid,
    "FirstName" character varying(100) COLLATE pg_catalog."default" NOT NULL,
    "LastName" character varying(100) COLLATE pg_catalog."default" NOT NULL,
    "BirthDate" timestamp without time zone NOT NULL,
    "SSN" character varying(15) COLLATE pg_catalog."default",
    "PMSSourceSystem" integer NOT NULL,
    "ExtraProperties" text COLLATE pg_catalog."default" NOT NULL,
    "ConcurrencyStamp" character varying(40) COLLATE pg_catalog."default" NOT NULL,
    "CreationTime" timestamp without time zone NOT NULL,
    "CreatorId" uuid,
    "LastModificationTime" timestamp without time zone,
    "LastModifierId" uuid,
    "ClinicId" uuid,
    "MiddleName" character varying(100) COLLATE pg_catalog."default",
    "PMSPatientId" character varying(100) COLLATE pg_catalog."default" NOT NULL DEFAULT ''::character varying,
    "PreferredName" character varying(100) COLLATE pg_catalog."default",
    "Status" integer NOT NULL DEFAULT 0,
    "Gender" integer NOT NULL DEFAULT 0,
    "MaritalStatus" integer NOT NULL DEFAULT 0,
    "ProviderId" uuid,
    CONSTRAINT "PK_Patients" PRIMARY KEY ("Id")
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public."Patients"
    OWNER to postgres;
-- Index: IX_Patients_TenantId_ClinicId_PMSPatientId_PMSSourceSystem

-- DROP INDEX IF EXISTS public."IX_Patients_TenantId_ClinicId_PMSPatientId_PMSSourceSystem";

CREATE UNIQUE INDEX IF NOT EXISTS "IX_Patients_TenantId_ClinicId_PMSPatientId_PMSSourceSystem"
    ON public."Patients" USING btree
    ("TenantId" ASC NULLS LAST, "ClinicId" ASC NULLS LAST, "PMSPatientId" COLLATE pg_catalog."default" ASC NULLS LAST, "PMSSourceSystem" ASC NULLS LAST)
    TABLESPACE pg_default;

-- Table: public.PatientProcedures

-- DROP TABLE IF EXISTS public."PatientProcedures";

CREATE TABLE IF NOT EXISTS public."PatientProcedures"
(
    "Id" uuid NOT NULL,
    "PMSProcedureId" character varying(100) COLLATE pg_catalog."default" NOT NULL,
    "PatientId" uuid NOT NULL,
    "PMSProcCode" character varying(20) COLLATE pg_catalog."default" NOT NULL,
    "ProcedureDate" timestamp without time zone NOT NULL,
    "ToothNum" character varying(10) COLLATE pg_catalog."default",
    "Surf" character varying(10) COLLATE pg_catalog."default",
    "Fee" numeric NOT NULL,
    "ExtraProperties" text COLLATE pg_catalog."default" NOT NULL,
    "ConcurrencyStamp" character varying(40) COLLATE pg_catalog."default" NOT NULL,
    "CreationTime" timestamp without time zone NOT NULL,
    "CreatorId" uuid,
    "LastModificationTime" timestamp without time zone,
    "LastModifierId" uuid,
    "TenantId" uuid,
    "ClinicId" uuid,
    "PMSSourceSystem" integer NOT NULL,
    "AppointmentId" uuid,
    "Status" integer,
    "Descript" text COLLATE pg_catalog."default" NOT NULL DEFAULT ''::text,
    "ProviderId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
    "Dx" integer,
    "DxName" character varying(50) COLLATE pg_catalog."default",
    CONSTRAINT "PK_PatientProcedures" PRIMARY KEY ("Id")
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public."PatientProcedures"
    OWNER to postgres;
-- Index: IX_PatientProcedure_TenantId_ClinicId_PMSProcedureId_PMSSourceS

-- DROP INDEX IF EXISTS public."IX_PatientProcedure_TenantId_ClinicId_PMSProcedureId_PMSSourceS";

CREATE UNIQUE INDEX IF NOT EXISTS "IX_PatientProcedure_TenantId_ClinicId_PMSProcedureId_PMSSourceS"
    ON public."PatientProcedures" USING btree
    ("TenantId" ASC NULLS LAST, "ClinicId" ASC NULLS LAST, "PMSProcedureId" COLLATE pg_catalog."default" ASC NULLS LAST, "PMSSourceSystem" ASC NULLS LAST)
    TABLESPACE pg_default;

-- Table: public.PatientPaymentPlans

-- DROP TABLE IF EXISTS public."PatientPaymentPlans";

CREATE TABLE IF NOT EXISTS public."PatientPaymentPlans"
(
    "Id" uuid NOT NULL,
    "PMSPaymentPlanID" character varying(100) COLLATE pg_catalog."default" NOT NULL,
    "PatientId" uuid NOT NULL,
    "GuarantorId" uuid NOT NULL,
    "StartDate" timestamp without time zone NOT NULL,
    "TotalPrincipal" numeric NOT NULL,
    "APR" numeric NOT NULL,
    "Frequency" character varying(30) COLLATE pg_catalog."default",
    "InstallmentCount" integer NOT NULL,
    "Status" character varying(20) COLLATE pg_catalog."default",
    "Note" text COLLATE pg_catalog."default",
    "ExtraProperties" text COLLATE pg_catalog."default" NOT NULL,
    "ConcurrencyStamp" character varying(40) COLLATE pg_catalog."default" NOT NULL,
    "CreationTime" timestamp without time zone NOT NULL,
    "CreatorId" uuid,
    "LastModificationTime" timestamp without time zone,
    "LastModifierId" uuid,
    "TenantId" uuid,
    "ClinicId" uuid,
    "PMSSourceSystem" integer NOT NULL,
    CONSTRAINT "PK_PatientPaymentPlans" PRIMARY KEY ("Id")
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public."PatientPaymentPlans"
    OWNER to postgres;
-- Index: IX_PatientPaymentPlan_TenantId_ClinicId_PMSPaymentPlanID_PMSSou

-- DROP INDEX IF EXISTS public."IX_PatientPaymentPlan_TenantId_ClinicId_PMSPaymentPlanID_PMSSou";

CREATE UNIQUE INDEX IF NOT EXISTS "IX_PatientPaymentPlan_TenantId_ClinicId_PMSPaymentPlanID_PMSSou"
    ON public."PatientPaymentPlans" USING btree
    ("TenantId" ASC NULLS LAST, "ClinicId" ASC NULLS LAST, "PMSPaymentPlanID" COLLATE pg_catalog."default" ASC NULLS LAST, "PMSSourceSystem" ASC NULLS LAST)
    TABLESPACE pg_default;

-- Table: public.PatientPaymentPlanCharges

-- DROP TABLE IF EXISTS public."PatientPaymentPlanCharges";

CREATE TABLE IF NOT EXISTS public."PatientPaymentPlanCharges"
(
    "Id" uuid NOT NULL,
    "PMSPaymentPlanChargeID" character varying(100) COLLATE pg_catalog."default" NOT NULL,
    "PaymentPlanId" uuid NOT NULL,
    "ChargeDate" timestamp without time zone NOT NULL,
    "PrincipalAmount" numeric NOT NULL,
    "InterestAmount" numeric NOT NULL,
    "ProviderId" uuid NOT NULL,
    "ProcedureId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
    "Note" text COLLATE pg_catalog."default",
    "ExtraProperties" text COLLATE pg_catalog."default" NOT NULL,
    "ConcurrencyStamp" character varying(40) COLLATE pg_catalog."default" NOT NULL,
    "CreationTime" timestamp without time zone NOT NULL,
    "CreatorId" uuid,
    "LastModificationTime" timestamp without time zone,
    "LastModifierId" uuid,
    "TenantId" uuid,
    "ClinicId" uuid,
    "PMSSourceSystem" integer NOT NULL,
    "PatientId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
    CONSTRAINT "PK_PatientPaymentPlanCharges" PRIMARY KEY ("Id")
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public."PatientPaymentPlanCharges"
    OWNER to postgres;
-- Index: IX_PatientPaymentPlanCharge_TenantId_ClinicId_PMSPaymentPlanCha

-- DROP INDEX IF EXISTS public."IX_PatientPaymentPlanCharge_TenantId_ClinicId_PMSPaymentPlanCha";

CREATE UNIQUE INDEX IF NOT EXISTS "IX_PatientPaymentPlanCharge_TenantId_ClinicId_PMSPaymentPlanCha"
    ON public."PatientPaymentPlanCharges" USING btree
    ("TenantId" ASC NULLS LAST, "ClinicId" ASC NULLS LAST, "PMSPaymentPlanChargeID" COLLATE pg_catalog."default" ASC NULLS LAST, "PMSSourceSystem" ASC NULLS LAST)
    TABLESPACE pg_default;



Enums If Required

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DentPal.Patients;


public enum ContactTypeEnum
{
    Email = 0,
    Mobile = 1,
    HomePhone = 2
}

public enum PatientGuarantorRolesEnum
{
    Billing = 0,
    Guardian = 1,
    Emergency = 2,
    Other = 3
}


public enum PatientLedgerEntryTypeEnum
{
    None = 0,
    Procedure = 1,
    Payment = 2,
    InsurancePayment = 3,
    Adjustment = 4,
    Refund = 5,
    WriteOff = 6
}


public enum TreatmentAccountingLedgerEntryTypeDto
{

    None = 0,
    Procedure = 1,
    Payment = 2,
    InsurancePayment = 3,
    Adjustment = 4,
    Refund = 5,
    WriteOff = 6,
    paymentPlan = 7,
    paymentPlanCharge = 8,
}
public enum PatientLedgerCategoryEnum
{
    None = 0,
    Charge = 1,
    Payment = 2,
    Adjustment = 3,
    IsurancePayment = 4
}
public enum TreatmentAccountingLedgerCategoryDto
{

    None = 0,
    Charge = 1,
    Payment = 2,
    Adjustment = 3,
    IsurancePayment = 4,
    PaymentPlan = 5,
}

public enum PatientProviderTypeEnum
{
    Dentist = 1,
    Hygienist = 2,
    Assistant = 3,
    Other = 4

}

public enum GenderEnum
{
    Unknown = 0,
    Male = 1,
    Female = 2,
    Other = 3,
}
public enum MaritalStatusEnum
{
    Uknown = 0,
    Single = 1,
    Married = 2,
    Widowed = 3,
    Divorced = 4,
    Child = 5
}

public enum ProcedureStatusEnum
{
    /// <summary>
    /// Used when the procedure status is not known or doesn't match any predefined state.
    /// </summary>
    Unknown,                // fallback for unexpected values
    /// <summary>
    /// The procedure is planned for the future but not yet completed.
    /// </summary>
    TreatmentPlanned,         // "TP", "P", "PL"

    /// <summary>
    /// The procedure was completed at this clinic.
    /// </summary>
    Completed,                // "C", "CM"

    /// <summary>
    /// The procedure was done in the past by the current provider.
    /// </summary>
    ExistingCurrentProvider,  // "EC", "EX" (same provider)

    /// <summary>
    /// The procedure was done in the past by another provider.
    /// </summary>
    ExistingOtherProvider,    // "EO", "EX" (other provider)

    /// <summary>
    /// The patient was referred to an external provider for this procedure.
    /// </summary>
    ReferredOut,              // "R", "RFO"

    /// <summary>
    /// The procedure was deleted or voided in the chart.
    /// </summary>
    Deleted,                  // "D", "DEL"

    /// <summary>
    /// A condition was observed, but no procedure is planned yet.
    /// </summary>
    Condition,                // "Cn", "CON"

    /// <summary>
    /// The procedure was part of an old or replaced treatment plan.
    /// </summary>
    TreatmentPlanInactive,    // "TPi"

    /// <summary>
    /// The procedure was canceled by the clinic or patient before being completed.
    /// </summary>
    Canceled                 // "CA"


}


public enum PatientRelationshipEnum
{
    Self = 0,
    Spouse = 1,
    Father = 2,
    Mother = 3,
    Guardian = 4,
    Sibling = 5,
    Child = 6,
    Other = 7
}

public enum PatientStatusEnum
{
    Active = 0,
    Inactive = 1,
    Deceased = 2
}
Actions #4

Updated by RishiKesh Tuniki about 1 month ago

  • Tracker changed from Bug to Feature
  • Severity deleted (Select Severity)
Actions #5

Updated by RishiKesh Tuniki 27 days ago

  • Subtask #972 added
Actions #6

Updated by RishiKesh Tuniki 25 days ago

Make this Feature as similar to the Images and Perio Chart

Actions #7

Updated by RishiKesh Tuniki 25 days ago

add the paysplit and family modules table support for the carrier and payments

Actions #8

Updated by RishiKesh Tuniki 25 days ago

clipboard-202603261052-8bncb.png

Make this Feature as similar to the Images and Perio Chart -- Its A PopUp Screen with P-Tabview Support

Actions #9

Updated by Thuan L 22 days ago

  • Status changed from New to Resolved
Actions

Also available in: Atom PDF