@mindbill/angular 0.16.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1248,11 +1248,12 @@ class MindBillBillLifecycleComponent {
1248
1248
  payment = { amount: 0, method: "check", depositDate: new Date().toISOString().slice(0, 10) };
1249
1249
  review = { reason: "", payerClaimControlNumber: "", disputedAmount: 0 };
1250
1250
  reportStatusOptions = REPORT_BILL_STATUS_OPTIONS;
1251
+ report = { status: null, company: "", representativeName: "", representativeRole: "", phone: "", callReference: "", note: "" };
1252
+ reportContactSummary = "";
1251
1253
  duplicateDelivery = null;
1252
1254
  duplicateDeliveryError = "";
1255
+ duplicateDraftInput = null;
1253
1256
  duplicate = { route: "ebill", faxNumber: "", email: "", mailingAddress: "" };
1254
- report = { status: null, company: "", representativeName: "", representativeRole: "", phone: "", callReference: "", note: "" };
1255
- reportContactSummary = "";
1256
1257
  correctionInitialBill = null;
1257
1258
  correctionAttachments = [];
1258
1259
  correctionAttentionFields = [];
@@ -1300,57 +1301,12 @@ class MindBillBillLifecycleComponent {
1300
1301
  else if (action === "close")
1301
1302
  this.panel = "close";
1302
1303
  else if (action === "send_duplicate")
1303
- this.beginSendDuplicate();
1304
+ this.prepareDuplicate(data);
1304
1305
  else if (action === "report_bill_status")
1305
1306
  this.beginReportStatus(data);
1306
1307
  else if (action === "view_eor" && data.eors[0])
1307
1308
  void this.openEor(data.eors[0].id, data.eors[0].filename);
1308
1309
  }
1309
- beginSendDuplicate() { this.panel = "duplicate"; this.loadDuplicateDelivery(); }
1310
- loadDuplicateDelivery() {
1311
- this.duplicateDeliveryError = "";
1312
- if (this.duplicateDelivery)
1313
- return;
1314
- this.store.getDeliveryOptions().then((delivery) => {
1315
- this.duplicateDelivery = delivery;
1316
- this.duplicate.route = delivery.recommended.route;
1317
- this.duplicate.faxNumber = delivery.contacts.faxNumber || "";
1318
- this.duplicate.email = delivery.contacts.claimsEmail || "";
1319
- this.duplicate.mailingAddress = delivery.contacts.mailingAddress || "";
1320
- }).catch((cause) => {
1321
- this.duplicateDeliveryError = cause instanceof Error ? cause.message : "Delivery options could not be loaded.";
1322
- });
1323
- }
1324
- duplicateRoutes() {
1325
- const seen = new Set();
1326
- return (this.duplicateDelivery?.options ?? []).filter((option) => {
1327
- if (seen.has(option.route))
1328
- return false;
1329
- seen.add(option.route);
1330
- return true;
1331
- });
1332
- }
1333
- duplicateReady() {
1334
- if (this.duplicate.route === "fax")
1335
- return this.duplicate.faxNumber.replace(/\D/g, "").length >= 10;
1336
- if (this.duplicate.route === "email")
1337
- return this.duplicate.email.includes("@");
1338
- if (this.duplicate.route === "mail")
1339
- return this.duplicate.mailingAddress.trim().length > 2;
1340
- return true;
1341
- }
1342
- async sendDuplicate() {
1343
- if (!this.duplicateDelivery || !this.duplicateReady())
1344
- return;
1345
- const route = this.duplicate.route;
1346
- const destination = route === "fax" ? { faxNumber: this.duplicate.faxNumber.trim() }
1347
- : route === "email" ? { email: this.duplicate.email.trim() }
1348
- : route === "mail" ? { mailingAddress: this.duplicate.mailingAddress.trim() }
1349
- : null;
1350
- await this.store.sendDuplicateBill({ route, ...(destination ? { destination } : {}) });
1351
- this.panel = "";
1352
- this.notice = "Duplicate bill sent.";
1353
- }
1354
1310
  beginReportStatus(data) {
1355
1311
  const contacts = reportBillStatusContacts(data.delivery);
1356
1312
  const parts = [];
@@ -1385,7 +1341,7 @@ class MindBillBillLifecycleComponent {
1385
1341
  async openAttachment(id, filename) { this.openBlob(await this.store.getAttachment(id), filename); }
1386
1342
  async openEor(id, filename) { this.openBlob(await this.store.getEor(id), filename); }
1387
1343
  async postPayment() { await this.store.postPayment(this.payment); this.panel = ""; this.notice = "Payment posted."; }
1388
- async submitReview() { await this.store.submitSecondReview({ ...this.review, disputedAmount: this.review.disputedAmount > 0 ? this.review.disputedAmount : undefined, attachmentIds: this.store.data()?.bill.attachments.map((document) => document.id) ?? [], route: "ebill" }); this.panel = ""; this.notice = "Second Review submitted."; }
1344
+ async submitReview() { await this.store.submitSecondReview({ ...this.review, ...(this.review.disputedAmount > 0 ? { disputedAmount: this.review.disputedAmount } : {}), attachmentIds: this.store.data()?.bill.attachments.map((document) => document.id) ?? [], route: "ebill" }); this.panel = ""; this.notice = "Second Review submitted."; }
1389
1345
  async closeBill() { if (!this.closeReason.trim())
1390
1346
  return; await this.store.closeBill({ reason: this.closeReason }); this.panel = ""; this.notice = "Bill closed."; }
1391
1347
  resubmitFromForm = async (input) => {
@@ -1397,6 +1353,66 @@ class MindBillBillLifecycleComponent {
1397
1353
  return { billId: data.bill.id, bill: { id: data.bill.id, state: data.lifecycle.state } };
1398
1354
  };
1399
1355
  finishCorrection() { this.panel = ""; this.correctionReason = ""; this.notice = "Corrected submission sent."; }
1356
+ duplicateFromForm = async (input) => {
1357
+ this.duplicateDraftInput = input;
1358
+ this.panel = "duplicate_route";
1359
+ await this.loadDuplicateDelivery();
1360
+ const billId = this.store.data()?.bill.id || this.billId;
1361
+ return { billId, bill: { id: billId, state: "draft" } };
1362
+ };
1363
+ finishDuplicate() { }
1364
+ async loadDuplicateDelivery() {
1365
+ this.duplicateDeliveryError = "";
1366
+ if (this.duplicateDelivery)
1367
+ return;
1368
+ try {
1369
+ const delivery = await this.store.getDeliveryOptions();
1370
+ this.duplicateDelivery = delivery;
1371
+ this.duplicate.route = delivery.recommended.route;
1372
+ this.duplicate.faxNumber = delivery.contacts.faxNumber || "";
1373
+ this.duplicate.email = delivery.contacts.claimsEmail || "";
1374
+ this.duplicate.mailingAddress = delivery.contacts.mailingAddress || "";
1375
+ }
1376
+ catch (cause) {
1377
+ this.duplicateDeliveryError = cause instanceof Error ? cause.message : "Delivery options could not be loaded.";
1378
+ }
1379
+ }
1380
+ duplicateRoutes() {
1381
+ const seen = new Set();
1382
+ return (this.duplicateDelivery?.options ?? []).filter((option) => {
1383
+ if (seen.has(option.route))
1384
+ return false;
1385
+ seen.add(option.route);
1386
+ return true;
1387
+ });
1388
+ }
1389
+ duplicateReady() {
1390
+ if (!this.duplicateDraftInput)
1391
+ return false;
1392
+ if (this.duplicate.route === "fax")
1393
+ return this.duplicate.faxNumber.replace(/\D/g, "").length >= 10;
1394
+ if (this.duplicate.route === "email")
1395
+ return this.duplicate.email.includes("@");
1396
+ if (this.duplicate.route === "mail")
1397
+ return this.duplicate.mailingAddress.trim().length > 2;
1398
+ return true;
1399
+ }
1400
+ async sendDuplicate() {
1401
+ if (!this.duplicateDelivery || !this.duplicateDraftInput || !this.duplicateReady())
1402
+ return;
1403
+ const route = this.duplicate.route;
1404
+ const destination = route === "fax" ? { faxNumber: this.duplicate.faxNumber.trim() }
1405
+ : route === "email" ? { email: this.duplicate.email.trim() }
1406
+ : route === "mail" ? { mailingAddress: this.duplicate.mailingAddress.trim() }
1407
+ : null;
1408
+ await this.store.sendDuplicateBill({
1409
+ bill: this.duplicateDraftInput.bill,
1410
+ ...(this.duplicateDraftInput.documents?.length ? { documents: this.duplicateDraftInput.documents } : {}),
1411
+ submission: { route, ...(destination ? { destination } : {}) },
1412
+ });
1413
+ this.panel = "";
1414
+ this.notice = "Duplicate bill sent.";
1415
+ }
1400
1416
  // "Submit New Bill" from a CLOSED bill: same prefilled submission form as the
1401
1417
  // correction flow, but the server keeps the closed bill closed and creates a
1402
1418
  // fresh original linked to it (both records chain in the submissions timeline).
@@ -1425,6 +1441,22 @@ class MindBillBillLifecycleComponent {
1425
1441
  this.correctionReason = "";
1426
1442
  this.panel = "resubmit";
1427
1443
  }
1444
+ prepareDuplicate(data) {
1445
+ this.correctionInitialBill = correctionBill(data);
1446
+ this.correctionAttachments = data.bill.attachments.map((attachment) => ({
1447
+ filename: attachment.filename,
1448
+ documentType: correctionDocumentType(attachment.documentType),
1449
+ ...(attachment.description ? { description: attachment.description } : {}),
1450
+ ...(attachment.reportType ? { reportTypeCode: attachment.reportType } : {}),
1451
+ loadBlob: () => this.store.getAttachment(attachment.id),
1452
+ ...(attachment.documentType === "w9" ? { locked: true, badge: "Auto-attached" } : {}),
1453
+ }));
1454
+ this.correctionAttentionFields = [];
1455
+ this.duplicateDraftInput = null;
1456
+ this.duplicateDelivery = null;
1457
+ this.duplicateDeliveryError = "";
1458
+ this.panel = "duplicate";
1459
+ }
1428
1460
  prepareNewBill(data) {
1429
1461
  this.correctionInitialBill = correctionBill(data);
1430
1462
  this.correctionAttachments = data.bill.attachments.map((attachment) => ({
@@ -1526,22 +1558,6 @@ class MindBillBillLifecycleComponent {
1526
1558
  @if (panel === 'payment') { <form class="card action-form" (submit)="$event.preventDefault(); postPayment()"><h3>Post payment</h3><label>Amount<input required type="number" min="0.01" step="0.01" [(ngModel)]="payment.amount" name="amount"></label><label>Method<select [(ngModel)]="payment.method" name="method"><option value="check">Check</option><option value="eft">EFT</option></select></label><label>Deposit date<input required type="date" [(ngModel)]="payment.depositDate" name="depositDate"></label><div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit">Post payment</button></div></form> }
1527
1559
  @if (panel === 'review') { <form class="card action-form" (submit)="$event.preventDefault(); submitReview()"><h3>Submit Second Bill Review</h3><label>Reason<textarea required [(ngModel)]="review.reason" name="reason"></textarea></label><label>Payer claim control number<input required [(ngModel)]="review.payerClaimControlNumber" name="control"></label><label>Disputed amount<input type="number" min="0" step="0.01" [(ngModel)]="review.disputedAmount" name="disputed"></label><div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit">Submit review</button></div></form> }
1528
1560
  @if (panel === 'close') { <form class="card action-form" (submit)="$event.preventDefault(); closeBill()"><h3>Close bill</h3><label>Reason<textarea required [(ngModel)]="closeReason" name="closeReason"></textarea></label><div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit">Close bill</button></div></form> }
1529
- @if (panel === 'duplicate') {
1530
- <form class="card action-form" (submit)="$event.preventDefault(); sendDuplicate()">
1531
- <h3>Send duplicate bill</h3>
1532
- @if (!duplicateDelivery) {
1533
- <p class="muted">{{ duplicateDeliveryError || 'Loading delivery options…' }}</p>
1534
- <div><button type="button" (click)="panel=''">Cancel</button>@if (duplicateDeliveryError) { <button class="primary" type="button" (click)="loadDuplicateDelivery()">Try again</button> }</div>
1535
- } @else {
1536
- <p class="muted">Resend the unchanged submission packet to {{ duplicateDelivery.payerName }}.</p>
1537
- <label>Delivery method<select [(ngModel)]="duplicate.route" name="duplicateRoute">@for (option of duplicateRoutes(); track option.route) { <option [ngValue]="option.route">{{ option.label }}</option> }</select></label>
1538
- @if (duplicate.route === 'fax') { <label>Fax number<input required [(ngModel)]="duplicate.faxNumber" name="duplicateFax" placeholder="(000) 000-0000"></label> }
1539
- @if (duplicate.route === 'email') { <label>Email<input required type="email" [(ngModel)]="duplicate.email" name="duplicateEmail" placeholder="claims@payer.example"></label> }
1540
- @if (duplicate.route === 'mail') { <label>Mailing address<textarea required [(ngModel)]="duplicate.mailingAddress" name="duplicateMail"></textarea></label> }
1541
- <div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit" [disabled]="store.mutating() || !duplicateReady()">Send duplicate</button></div>
1542
- }
1543
- </form>
1544
- }
1545
1561
  @if (panel === 'report_status') {
1546
1562
  <form class="card action-form" (submit)="$event.preventDefault(); reportStatus()">
1547
1563
  <h3>Report Bill Status</h3>
@@ -1587,6 +1603,43 @@ class MindBillBillLifecycleComponent {
1587
1603
  </section>
1588
1604
  </div>
1589
1605
  }
1606
+ @if (panel === 'duplicate' && correctionInitialBill) {
1607
+ <div class="correction-backdrop" (mousedown)="closeCorrectionBackdrop($event)">
1608
+ <section class="correction-dialog" role="dialog" aria-modal="true" aria-label="Send duplicate bill">
1609
+ <button type="button" class="dialog-close" aria-label="Close" (click)="panel=''">×</button>
1610
+ <header><h2>Send duplicate bill</h2><p>The original submission remains immutable. Review or edit this fresh bill before choosing its delivery route and confirming transmission.</p></header>
1611
+ <mindbill-bill-submission
1612
+ [initialBill]="correctionInitialBill"
1613
+ [attachments]="correctionAttachments"
1614
+ [appearance]="appearance"
1615
+ [sessionEndpoint]="sessionEndpoint"
1616
+ [getSession]="getSession"
1617
+ [apiBaseUrl]="apiBaseUrl"
1618
+ [submitter]="duplicateFromForm"
1619
+ heading="Duplicate bill information"
1620
+ description="Review every field. The final step confirms the payer delivery route before the duplicate is sent."
1621
+ submitLabel="Continue to delivery"
1622
+ (submitted)="finishDuplicate()"
1623
+ />
1624
+ </section>
1625
+ </div>
1626
+ }
1627
+ @if (panel === 'duplicate_route') {
1628
+ <form class="card action-form" (submit)="$event.preventDefault(); sendDuplicate()">
1629
+ <h3>Confirm duplicate bill delivery</h3>
1630
+ @if (!duplicateDelivery) {
1631
+ <p class="muted">{{ duplicateDeliveryError || 'Loading delivery options…' }}</p>
1632
+ <div><button type="button" (click)="panel='duplicate'">Back</button>@if (duplicateDeliveryError) { <button class="primary" type="button" (click)="loadDuplicateDelivery()">Try again</button> }</div>
1633
+ } @else {
1634
+ <p class="muted">Confirm how this edited duplicate will be delivered to {{ duplicateDelivery.payerName }}.</p>
1635
+ <label>Delivery method<select [(ngModel)]="duplicate.route" name="duplicateRoute">@for (option of duplicateRoutes(); track option.route) { <option [ngValue]="option.route">{{ option.label }}</option> }</select></label>
1636
+ @if (duplicate.route === 'fax') { <label>Fax number<input required [(ngModel)]="duplicate.faxNumber" name="duplicateFax" placeholder="(000) 000-0000"></label> }
1637
+ @if (duplicate.route === 'email') { <label>Email<input required type="email" [(ngModel)]="duplicate.email" name="duplicateEmail" placeholder="claims@payer.example"></label> }
1638
+ @if (duplicate.route === 'mail') { <label>Mailing address<textarea required [(ngModel)]="duplicate.mailingAddress" name="duplicateMail"></textarea></label> }
1639
+ <div><button type="button" (click)="panel='duplicate'">Back</button><button class="primary" type="submit" [disabled]="store.mutating() || !duplicateReady()">Send duplicate</button></div>
1640
+ }
1641
+ </form>
1642
+ }
1590
1643
  @if (panel === 'submit_new_bill' && correctionInitialBill) {
1591
1644
  <div class="correction-backdrop" (mousedown)="closeCorrectionBackdrop($event)">
1592
1645
  <section class="correction-dialog" role="dialog" aria-modal="true" aria-label="Submit New Bill">
@@ -1699,22 +1752,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
1699
1752
  @if (panel === 'payment') { <form class="card action-form" (submit)="$event.preventDefault(); postPayment()"><h3>Post payment</h3><label>Amount<input required type="number" min="0.01" step="0.01" [(ngModel)]="payment.amount" name="amount"></label><label>Method<select [(ngModel)]="payment.method" name="method"><option value="check">Check</option><option value="eft">EFT</option></select></label><label>Deposit date<input required type="date" [(ngModel)]="payment.depositDate" name="depositDate"></label><div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit">Post payment</button></div></form> }
1700
1753
  @if (panel === 'review') { <form class="card action-form" (submit)="$event.preventDefault(); submitReview()"><h3>Submit Second Bill Review</h3><label>Reason<textarea required [(ngModel)]="review.reason" name="reason"></textarea></label><label>Payer claim control number<input required [(ngModel)]="review.payerClaimControlNumber" name="control"></label><label>Disputed amount<input type="number" min="0" step="0.01" [(ngModel)]="review.disputedAmount" name="disputed"></label><div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit">Submit review</button></div></form> }
1701
1754
  @if (panel === 'close') { <form class="card action-form" (submit)="$event.preventDefault(); closeBill()"><h3>Close bill</h3><label>Reason<textarea required [(ngModel)]="closeReason" name="closeReason"></textarea></label><div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit">Close bill</button></div></form> }
1702
- @if (panel === 'duplicate') {
1703
- <form class="card action-form" (submit)="$event.preventDefault(); sendDuplicate()">
1704
- <h3>Send duplicate bill</h3>
1705
- @if (!duplicateDelivery) {
1706
- <p class="muted">{{ duplicateDeliveryError || 'Loading delivery options…' }}</p>
1707
- <div><button type="button" (click)="panel=''">Cancel</button>@if (duplicateDeliveryError) { <button class="primary" type="button" (click)="loadDuplicateDelivery()">Try again</button> }</div>
1708
- } @else {
1709
- <p class="muted">Resend the unchanged submission packet to {{ duplicateDelivery.payerName }}.</p>
1710
- <label>Delivery method<select [(ngModel)]="duplicate.route" name="duplicateRoute">@for (option of duplicateRoutes(); track option.route) { <option [ngValue]="option.route">{{ option.label }}</option> }</select></label>
1711
- @if (duplicate.route === 'fax') { <label>Fax number<input required [(ngModel)]="duplicate.faxNumber" name="duplicateFax" placeholder="(000) 000-0000"></label> }
1712
- @if (duplicate.route === 'email') { <label>Email<input required type="email" [(ngModel)]="duplicate.email" name="duplicateEmail" placeholder="claims@payer.example"></label> }
1713
- @if (duplicate.route === 'mail') { <label>Mailing address<textarea required [(ngModel)]="duplicate.mailingAddress" name="duplicateMail"></textarea></label> }
1714
- <div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit" [disabled]="store.mutating() || !duplicateReady()">Send duplicate</button></div>
1715
- }
1716
- </form>
1717
- }
1718
1755
  @if (panel === 'report_status') {
1719
1756
  <form class="card action-form" (submit)="$event.preventDefault(); reportStatus()">
1720
1757
  <h3>Report Bill Status</h3>
@@ -1760,6 +1797,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
1760
1797
  </section>
1761
1798
  </div>
1762
1799
  }
1800
+ @if (panel === 'duplicate' && correctionInitialBill) {
1801
+ <div class="correction-backdrop" (mousedown)="closeCorrectionBackdrop($event)">
1802
+ <section class="correction-dialog" role="dialog" aria-modal="true" aria-label="Send duplicate bill">
1803
+ <button type="button" class="dialog-close" aria-label="Close" (click)="panel=''">×</button>
1804
+ <header><h2>Send duplicate bill</h2><p>The original submission remains immutable. Review or edit this fresh bill before choosing its delivery route and confirming transmission.</p></header>
1805
+ <mindbill-bill-submission
1806
+ [initialBill]="correctionInitialBill"
1807
+ [attachments]="correctionAttachments"
1808
+ [appearance]="appearance"
1809
+ [sessionEndpoint]="sessionEndpoint"
1810
+ [getSession]="getSession"
1811
+ [apiBaseUrl]="apiBaseUrl"
1812
+ [submitter]="duplicateFromForm"
1813
+ heading="Duplicate bill information"
1814
+ description="Review every field. The final step confirms the payer delivery route before the duplicate is sent."
1815
+ submitLabel="Continue to delivery"
1816
+ (submitted)="finishDuplicate()"
1817
+ />
1818
+ </section>
1819
+ </div>
1820
+ }
1821
+ @if (panel === 'duplicate_route') {
1822
+ <form class="card action-form" (submit)="$event.preventDefault(); sendDuplicate()">
1823
+ <h3>Confirm duplicate bill delivery</h3>
1824
+ @if (!duplicateDelivery) {
1825
+ <p class="muted">{{ duplicateDeliveryError || 'Loading delivery options…' }}</p>
1826
+ <div><button type="button" (click)="panel='duplicate'">Back</button>@if (duplicateDeliveryError) { <button class="primary" type="button" (click)="loadDuplicateDelivery()">Try again</button> }</div>
1827
+ } @else {
1828
+ <p class="muted">Confirm how this edited duplicate will be delivered to {{ duplicateDelivery.payerName }}.</p>
1829
+ <label>Delivery method<select [(ngModel)]="duplicate.route" name="duplicateRoute">@for (option of duplicateRoutes(); track option.route) { <option [ngValue]="option.route">{{ option.label }}</option> }</select></label>
1830
+ @if (duplicate.route === 'fax') { <label>Fax number<input required [(ngModel)]="duplicate.faxNumber" name="duplicateFax" placeholder="(000) 000-0000"></label> }
1831
+ @if (duplicate.route === 'email') { <label>Email<input required type="email" [(ngModel)]="duplicate.email" name="duplicateEmail" placeholder="claims@payer.example"></label> }
1832
+ @if (duplicate.route === 'mail') { <label>Mailing address<textarea required [(ngModel)]="duplicate.mailingAddress" name="duplicateMail"></textarea></label> }
1833
+ <div><button type="button" (click)="panel='duplicate'">Back</button><button class="primary" type="submit" [disabled]="store.mutating() || !duplicateReady()">Send duplicate</button></div>
1834
+ }
1835
+ </form>
1836
+ }
1763
1837
  @if (panel === 'submit_new_bill' && correctionInitialBill) {
1764
1838
  <div class="correction-backdrop" (mousedown)="closeCorrectionBackdrop($event)">
1765
1839
  <section class="correction-dialog" role="dialog" aria-modal="true" aria-label="Submit New Bill">