mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
feat: rendering pdf templates on the server-side
This commit is contained in:
@@ -1,81 +1,205 @@
|
|||||||
extends ../PaperTemplateLayout.pug
|
extends ../PaperTemplateLayout.pug
|
||||||
|
|
||||||
block head
|
block head
|
||||||
style
|
- var prefix = 'bc'
|
||||||
if (isRtl)
|
style.
|
||||||
include ../../css/modules/credit-rtl.css
|
.#{prefix}-root {
|
||||||
else
|
color: #111;
|
||||||
include ../../css/modules/credit.css
|
padding: 24px 30px;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
|
||||||
|
}
|
||||||
|
.#{prefix}-big-title {
|
||||||
|
font-size: 60px;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-logo-wrap {
|
||||||
|
height: 120px;
|
||||||
|
width: 120px;
|
||||||
|
position: absolute;
|
||||||
|
right: 26px;
|
||||||
|
top: 26px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item__label {
|
||||||
|
min-width: 120px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item__value {
|
||||||
|
/* Styles for the term value */
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{prefix}-group {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-pack: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.#{prefix}-address {
|
||||||
|
/* Styles for each address block */
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{prefix}-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
text-align: left;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header {
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
padding: 2px 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header:first-of-type{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header:last-of-type{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell {
|
||||||
|
border-bottom: 1px solid #F6F6F6;
|
||||||
|
padding: 12px 10px;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell:first-of-type{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell:last-of-type {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{prefix}-totals {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: auto;
|
||||||
|
width: 300px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--border-gray {
|
||||||
|
border-bottom: 1px solid #DADADA;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--border-dark {
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--font-weight-bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item-label {
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item-amount {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{prefix}-statement {
|
||||||
|
/* Styles for customer note/terms statement section */
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{prefix}-statement__label {
|
||||||
|
/* Styles for statement label */
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{prefix}-statement__value {
|
||||||
|
/* Styles for statement value */
|
||||||
|
}
|
||||||
|
|
||||||
block content
|
block content
|
||||||
div.credit
|
div(class=`${prefix}-root`)
|
||||||
div.credit__header
|
div(class=`${prefix}-big-title`) Credit Note
|
||||||
div.paper
|
|
||||||
h1.title #{__('credit.paper.credit_note')}
|
|
||||||
if creditNote.creditNoteNumber
|
|
||||||
span.creditNoteNumber #{creditNote.creditNoteNumber}
|
|
||||||
|
|
||||||
div.organization
|
if showCompanyLogo
|
||||||
h3.title #{organizationName}
|
div(class=`${prefix}-logo-wrap`)
|
||||||
if organizationEmail
|
img(src=companyLogo alt=`Company Logo`)
|
||||||
span.email #{organizationEmail}
|
|
||||||
|
div(class=`${prefix}-terms-list`)
|
||||||
|
if showCreditNoteNumber
|
||||||
|
div(class=`${prefix}-terms-item`)
|
||||||
|
div(class=`${prefix}-terms-item__label`) #{creditNoteNumberLabel}:
|
||||||
|
div(class=`${prefix}-terms-item__value`) #{creditNoteNumebr}
|
||||||
|
|
||||||
div.credit__full-amount
|
if showCreditNoteDate
|
||||||
div.label #{__('credit.paper.amount')}
|
div(class=`${prefix}-terms-item`)
|
||||||
div.amount #{creditNote.formattedAmount}
|
div(class=`${prefix}-terms-item__label`) #{creditNoteDateLabel}:
|
||||||
|
div(class=`${prefix}-terms-item__value`) #{creditNoteDate}
|
||||||
|
|
||||||
div.credit__meta
|
div(class=`${prefix}-group`)
|
||||||
div.credit__meta-item.credit__meta-item--amount
|
if showBilledFromAddress
|
||||||
span.label #{__('credit.paper.remaining')}
|
div(class=`${prefix}-address`)
|
||||||
span.value #{creditNote.formattedCreditsRemaining}
|
strong #{companyName}
|
||||||
|
each address in billedFromAddress
|
||||||
|
div #{address}
|
||||||
|
if showBilledToAddress
|
||||||
|
div(class=`${prefix}-address`)
|
||||||
|
strong #{billedToLabel}
|
||||||
|
each address in billedToAddress
|
||||||
|
div #{address}
|
||||||
|
|
||||||
div.credit__meta-item.credit__meta-item--billed-to
|
table(class=`${prefix}-table`)
|
||||||
span.label #{__("credit.paper.billed_to")}
|
thead(class=`${prefix}-table__header`)
|
||||||
span.value #{creditNote.customer.displayName}
|
tr
|
||||||
|
th #{'Item'}
|
||||||
div.credit__meta-item.credit__meta-item--credit-date
|
th #{'Description'}
|
||||||
span.label #{__("credit.paper.credit_date")}
|
th #{'Rate'}
|
||||||
span.value #{creditNote.formattedCreditNoteDate}
|
th #{'Total'}
|
||||||
|
|
||||||
div.credit__table
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th.item #{__("item_entry.paper.item_name")}
|
|
||||||
th.rate #{__("item_entry.paper.rate")}
|
|
||||||
th.quantity #{__("item_entry.paper.quantity")}
|
|
||||||
th.total #{__("item_entry.paper.total")}
|
|
||||||
tbody
|
tbody
|
||||||
each entry in creditNote.entries
|
each line in lines
|
||||||
tr
|
tr(class=`${prefix}-table__row`)
|
||||||
td.item
|
td #{line.item}
|
||||||
div.title=entry.item.name
|
td #{line.description}
|
||||||
span.description=entry.description
|
td(class=`${prefix}-table__column--right`) #{line.rate}
|
||||||
td.rate=entry.rate
|
td(class=`${prefix}-table__column--right`) #{line.total}
|
||||||
td.quantity=entry.quantity
|
|
||||||
td.total=entry.amount
|
|
||||||
|
|
||||||
div.credit__table-after
|
div(class=`${prefix}-totals`)
|
||||||
div.credit__table-total
|
if showSubtotal
|
||||||
table
|
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-gray`)
|
||||||
tbody
|
div #{subtotalLabel}:
|
||||||
tr.total
|
div #{subtotal}
|
||||||
td #{__('credit.paper.total')}
|
|
||||||
td #{creditNote.formattedAmount}
|
|
||||||
tr.payment-amount
|
|
||||||
td #{__('credit.paper.credits_used')}
|
|
||||||
td #{creditNote.formattedCreditsUsed}
|
|
||||||
tr.blanace-due
|
|
||||||
td #{__('credit.paper.credits_remaining')}
|
|
||||||
td #{creditNote.formattedCreditsRemaining}
|
|
||||||
|
|
||||||
div.credit__footer
|
if showTotal
|
||||||
if creditNote.termsConditions
|
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-dark`)
|
||||||
div.credit__conditions
|
div #{totalLabel}:
|
||||||
h3 #{__("credit.paper.terms_conditions")}
|
div #{total}
|
||||||
p #{creditNote.termsConditions}
|
|
||||||
|
|
||||||
if creditNote.note
|
if showCustomerNote
|
||||||
div.credit__notes
|
div(class=`${prefix}-statement`)
|
||||||
h3 #{__("credit.paper.notes")}
|
div(class=`${prefix}-statement__label`) #{customerNoteLabel}:
|
||||||
p #{creditNote.note}
|
div(class=`${prefix}-statement__value`) #{customerNote}
|
||||||
|
|
||||||
|
if showTermsConditions
|
||||||
|
div(class=`${prefix}-statement`)
|
||||||
|
div(class=`${prefix}-statement__label`) #{termsConditionsLabel}:
|
||||||
|
div(class=`${prefix}-statement__value`) #{termsConditions}
|
||||||
|
|||||||
@@ -1,82 +1,202 @@
|
|||||||
extends ../PaperTemplateLayout.pug
|
extends ../PaperTemplateLayout.pug
|
||||||
|
|
||||||
block head
|
block head
|
||||||
style
|
- var prefix = 'bc'
|
||||||
if (isRtl)
|
style.
|
||||||
include ../../css/modules/estimate-rtl.css
|
.#{prefix}-root {
|
||||||
else
|
color: #111;
|
||||||
include ../../css/modules/estimate.css
|
padding: 24px 30px;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
|
||||||
|
}
|
||||||
|
.#{prefix}-big-title {
|
||||||
|
font-size: 60px;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-logo-wrap {
|
||||||
|
height: 120px;
|
||||||
|
width: 120px;
|
||||||
|
position: absolute;
|
||||||
|
right: 26px;
|
||||||
|
top: 26px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item__label {
|
||||||
|
min-width: 120px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item__value {
|
||||||
|
}
|
||||||
|
.#{prefix}-address-section{
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-pack: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-address {
|
||||||
|
}
|
||||||
|
.#{prefix}-address__item {
|
||||||
|
}
|
||||||
|
.#{prefix}-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
text-align: left;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header {
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
padding: 2px 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header:first-of-type{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header:last-of-type{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell {
|
||||||
|
border-bottom: 1px solid #F6F6F6;
|
||||||
|
padding: 12px 10px;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell--right{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell:first-of-type{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell:last-of-type {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: auto;
|
||||||
|
width: 300px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--border-gray {
|
||||||
|
border-bottom: 1px solid #DADADA;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--border-dark {
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--font-weight-bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item-label {
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item-amount {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-statement {
|
||||||
|
}
|
||||||
|
.#{prefix}-statement__label {
|
||||||
|
}
|
||||||
|
.#{prefix}-statement__value {
|
||||||
|
}
|
||||||
|
|
||||||
block content
|
block content
|
||||||
div.estimate
|
div(class=`${prefix}-root`, style=`--invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor};`)
|
||||||
div.estimate__header
|
h1(class=`${prefix}-big-title`) Estimate
|
||||||
div.paper
|
|
||||||
h1.title #{__("estimate.paper.estimate")}
|
|
||||||
span.email #{saleEstimate.estimateNumber}
|
|
||||||
|
|
||||||
div.organization
|
if showCompanyLogo
|
||||||
h3.title #{organizationName}
|
div(class=`${prefix}-logo-wrap`)
|
||||||
if organizationEmail
|
img(alt="", src=companyLogo)
|
||||||
span.email #{organizationEmail}
|
|
||||||
|
|
||||||
div.estimate__estimate-amount
|
//- Terms List
|
||||||
div.label #{__('estimate.paper.estimate_amount')}
|
div(class=`${prefix}-terms`)
|
||||||
div.amount #{saleEstimate.formattedAmount}
|
if showEstimateNumber
|
||||||
|
div(class=`${prefix}-terms-item`)
|
||||||
|
div(class=`${prefix}-terms-item__label`) #{estimateNumberLabel}
|
||||||
|
div(class=`${prefix}-terms-item__value`) #{estimateNumebr}
|
||||||
|
if showEstimateDate
|
||||||
|
div(class=`${prefix}-terms-item`)
|
||||||
|
div(class=`${prefix}-terms-item__label`) #{estimateDateLabel}
|
||||||
|
div(class=`${prefix}-terms-item__value`) #{estimateDate}
|
||||||
|
if showExpirationDate
|
||||||
|
div(class=`${prefix}-terms-item`)
|
||||||
|
div(class=`${prefix}-terms-item__label`) #{expirationDateLabel}
|
||||||
|
div(class=`${prefix}-terms-item__value`) #{expirationDate}
|
||||||
|
|
||||||
div.estimate__meta
|
//- Addresses (Group section)
|
||||||
if saleEstimate.estimateNumber
|
div(class=`${prefix}-address-section`)
|
||||||
div.estimate__meta-item.estimate__meta-item--estimate-number
|
if showBilledFromAddress
|
||||||
span.label #{__("estimate.paper.estimate_number")}
|
div(class=`${prefix}-address`)
|
||||||
span.value #{saleEstimate.estimateNumber}
|
strong #{companyName}
|
||||||
|
each item in billedFromAddress
|
||||||
|
div(class=`${prefix}-address__item`) #{item}
|
||||||
|
|
||||||
div.estimate__meta-item.estimate__meta-item--billed-to
|
if showBilledToAddress
|
||||||
span.label #{__("estimate.paper.billed_to")}
|
div(class=`${prefix}-address`)
|
||||||
span.value #{saleEstimate.customer.displayName}
|
strong #{billedToLabel}
|
||||||
|
each item in billedToAddress
|
||||||
|
div(class=`${prefix}-address__item`) #{item}
|
||||||
|
|
||||||
div.estimate__meta-item.estimate__meta-item--estimate-date
|
//- Table section (Line items)
|
||||||
span.label #{__("estimate.paper.estimate_date")}
|
table(class=`${prefix}-table`)
|
||||||
span.value #{saleEstimate.formattedEstimateDate}
|
thead
|
||||||
|
tr
|
||||||
div.estimate__meta-item.estimate__meta-item--due-date
|
th(class=`${prefix}-table__header`) Item
|
||||||
span.label #{__("estimate.paper.expiration_date")}
|
th(class=`${prefix}-table__header`) Description
|
||||||
span.value #{saleEstimate.formattedExpirationDate}
|
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Rate
|
||||||
|
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Total
|
||||||
div.estimate__table
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th.item #{__("item_entry.paper.item_name")}
|
|
||||||
th.rate #{__("item_entry.paper.rate")}
|
|
||||||
th.quantity #{__("item_entry.paper.quantity")}
|
|
||||||
th.total #{__("item_entry.paper.total")}
|
|
||||||
tbody
|
tbody
|
||||||
each entry in saleEstimate.entries
|
each line in lines
|
||||||
tr
|
tr
|
||||||
td.item
|
td(class=`${prefix}-table__cell`) #{line.item}
|
||||||
div.title=entry.item.name
|
td(class=`${prefix}-table__cell`) #{line.description}
|
||||||
span.description=entry.description
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.rate}
|
||||||
td.rate=entry.rate
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.total}
|
||||||
td.quantity=entry.quantity
|
|
||||||
td.total=entry.amount
|
|
||||||
|
|
||||||
div.estimate__table-after
|
//- Totals section
|
||||||
div.estimate__table-total
|
div(class=`${prefix}-totals`)
|
||||||
table
|
if showSubtotal
|
||||||
tbody
|
div(class=`${prefix}-totals__item`)
|
||||||
tr.subtotal
|
div(class=`${prefix}-totals__item-label`) #{subtotalLabel}
|
||||||
td #{__('estimate.paper.subtotal')}
|
div(class=`${prefix}-totals__item-amount`) #{subtotal}
|
||||||
td #{saleEstimate.formattedAmount}
|
if showTotal
|
||||||
tr.total
|
div(class=`${prefix}-totals__item`)
|
||||||
td #{__('estimate.paper.total')}
|
div(class=`${prefix}-totals__item-label`) #{totalLabel}
|
||||||
td #{saleEstimate.formattedAmount}
|
div(class=`${prefix}-totals__item-amount`) #{total}
|
||||||
|
|
||||||
div.estimate__footer
|
//- Statements section
|
||||||
if saleEstimate.termsConditions
|
if showCustomerNote
|
||||||
div.estimate__conditions
|
div(class=`${prefix}-statement`)
|
||||||
h3 #{__("estimate.paper.conditions_title")}
|
div(class=`${prefix}-statement__label`) #{customerNoteLabel}
|
||||||
p #{saleEstimate.termsConditions}
|
div(class=`${prefix}-statement__value`) #{customerNote}
|
||||||
|
|
||||||
if saleEstimate.note
|
if showTermsConditions
|
||||||
div.estimate__notes
|
div(class=`${prefix}-statement`)
|
||||||
h3 #{__("estimate.paper.notes_title")}
|
div(class=`${prefix}-statement__label`) #{termsConditionsLabel}
|
||||||
p #{saleEstimate.note}
|
div(class=`${prefix}-statement__value`) #{termsConditions}
|
||||||
@@ -4,7 +4,6 @@ block head
|
|||||||
- var prefix = 'bc'
|
- var prefix = 'bc'
|
||||||
style.
|
style.
|
||||||
.#{prefix}-root {
|
.#{prefix}-root {
|
||||||
background-color: #fff;
|
|
||||||
color: #111;
|
color: #111;
|
||||||
padding: 24px 30px;
|
padding: 24px 30px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -25,7 +24,6 @@ block head
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: 26px;
|
right: 26px;
|
||||||
top: 26px;
|
top: 26px;
|
||||||
border-radius: 5px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.#{prefix}-details {
|
.#{prefix}-details {
|
||||||
@@ -122,7 +120,6 @@ block head
|
|||||||
}
|
}
|
||||||
.#{prefix}-totals__item--font-weight-bold {
|
.#{prefix}-totals__item--font-weight-bold {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
/* Additional styles for total items with bold font weight */
|
|
||||||
}
|
}
|
||||||
.#{prefix}-totals__item-label {
|
.#{prefix}-totals__item-label {
|
||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
@@ -143,6 +140,7 @@ block head
|
|||||||
block content
|
block content
|
||||||
//- block head
|
//- block head
|
||||||
div(class=`${prefix}-root`, style=`--invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor};`)
|
div(class=`${prefix}-root`, style=`--invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor};`)
|
||||||
|
|
||||||
//- Title and company logo
|
//- Title and company logo
|
||||||
h1(class=`${prefix}-big-title`) Invoice
|
h1(class=`${prefix}-big-title`) Invoice
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +1,187 @@
|
|||||||
extends ../PaperTemplateLayout.pug
|
extends ../PaperTemplateLayout.pug
|
||||||
|
|
||||||
block head
|
block head
|
||||||
style
|
- var prefix = 'bp3';
|
||||||
if (isRtl)
|
|
||||||
include ../../css/modules/payment-rtl.css
|
style.
|
||||||
else
|
.#{prefix}-root{
|
||||||
include ../../css/modules/payment.css
|
color: #111;
|
||||||
|
padding: 24px 30px;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
|
||||||
|
}
|
||||||
|
.#{prefix}-big-title{
|
||||||
|
font-size: 60px;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-logo-wrap{
|
||||||
|
height: 120px;
|
||||||
|
width: 120px;
|
||||||
|
position: absolute;
|
||||||
|
right: 26px;
|
||||||
|
top: 26px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-list{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item__label{
|
||||||
|
min-width: 120px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-group{
|
||||||
|
|
||||||
|
}
|
||||||
|
.#{prefix}-addresses{
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-pack: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-addresses > * {
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
.#{prefix}-address__label{
|
||||||
|
|
||||||
|
}
|
||||||
|
.#{prefix}-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
text-align: left;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header {
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
padding: 2px 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header:first-of-type{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header:last-of-type{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell {
|
||||||
|
border-bottom: 1px solid #F6F6F6;
|
||||||
|
padding: 12px 10px;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell:first-of-type{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell:last-of-type {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__column{
|
||||||
|
|
||||||
|
}
|
||||||
|
.#{prefix}-table__column--right{
|
||||||
|
|
||||||
|
}
|
||||||
|
.#{prefix}-totals {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: auto;
|
||||||
|
width: 300px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--gray-border {
|
||||||
|
border-bottom: 1px solid #DADADA;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--dark-border {
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item--bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item-label {
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__item-amount {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
block content
|
block content
|
||||||
div.payment
|
div(class=`${prefix}-root`)
|
||||||
div.payment__header
|
div(class=`${prefix}-big-title`) Payment
|
||||||
div.paper
|
|
||||||
h1.title #{__("payment.paper.payment_receipt")}
|
|
||||||
if paymentReceive.paymentReceiveNo
|
|
||||||
span.paymentNumber #{paymentReceive.paymentReceiveNo}
|
|
||||||
|
|
||||||
div.organization
|
if showCompanyLogo
|
||||||
h3.title #{organizationName}
|
div(class=`${prefix}-logo-wrap`)
|
||||||
if organizationEmail
|
img(src=companyLogo alt="Company Logo")
|
||||||
span.email #{organizationEmail}
|
|
||||||
|
div(class=`${prefix}-terms-list`)
|
||||||
|
if showPaymentReceivedNumber
|
||||||
|
div(class=`${prefix}-terms-item`)
|
||||||
|
div(class=`${prefix}-terms-item__label`) #{paymentReceivedNumberLabel}
|
||||||
|
div(class=`${prefix}-terms-item__value`) #{paymentReceivedNumebr}
|
||||||
|
|
||||||
div.payment__received-amount
|
if showPaymentReceivedDate
|
||||||
div.label #{__('payment.paper.amount_received')}
|
div(class=`${prefix}-terms-item`)
|
||||||
div.amount #{paymentReceive.formattedAmount}
|
div(class=`${prefix}-terms-item__label`) #{paymentReceivedDateLabel}
|
||||||
|
div(class=`${prefix}-terms-item__value`) #{paymentReceivedDate}
|
||||||
|
|
||||||
|
div(class=`${prefix}-addresses`)
|
||||||
|
if showBilledFromAddress
|
||||||
|
div(class=`${prefix}-address`)
|
||||||
|
strong(class=`${prefix}-address__item`) #{companyName}
|
||||||
|
each addressLine in billedFromAddress
|
||||||
|
div(class=`${prefix}-address__item`) #{addressLine}
|
||||||
|
|
||||||
div.payment__meta
|
if showBillingToAddress
|
||||||
div.payment__meta-item.payment__meta-item--billed-to
|
div(class=`${prefix}-address`)
|
||||||
span.label #{__("payment.paper.billed_to")}
|
strong(class=`${prefix}-address__item`) #{billedToLabel}
|
||||||
span.value #{paymentReceive.customer.displayName}
|
each addressLine in billedToAddress
|
||||||
|
div(class=`${prefix}-address__item`) #{addressLine}
|
||||||
|
|
||||||
div.payment__meta-item.payment__meta-item--payment-date
|
table(class=`${prefix}-table`)
|
||||||
span.label #{__("payment.paper.payment_date")}
|
thead
|
||||||
span.value #{paymentReceive.formattedPaymentDate}
|
tr
|
||||||
|
th(class=`${prefix}-table__header`) Invoice #
|
||||||
|
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Invoice Amount
|
||||||
|
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Paid Amount
|
||||||
|
|
||||||
div.payment__table
|
tbody
|
||||||
table
|
each line in lines
|
||||||
thead
|
tr
|
||||||
tr
|
td(class=`${prefix}-table__cell`) #{line.invoiceNumber}
|
||||||
th.item #{__("payment.paper.invoice_number")}
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.invoiceAmount}
|
||||||
th.date #{__("payment.paper.invoice_date")}
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.paidAmount}
|
||||||
th.invoiceAmount #{__("payment.paper.invoice_amount")}
|
|
||||||
th.paymentAmount #{__("payment.paper.payment_amount")}
|
|
||||||
tbody
|
|
||||||
each entry in paymentReceive.entries
|
|
||||||
tr
|
|
||||||
td.item=entry.invoice.invoiceNo
|
|
||||||
td.date=entry.invoice.invoiceDateFormatted
|
|
||||||
td.invoiceAmount=entry.invoice.totalFormatted
|
|
||||||
td.paymentAmount=entry.invoice.paymentAmountFormatted
|
|
||||||
|
|
||||||
div.payment__table-after
|
div(class=`${prefix}-totals`)
|
||||||
div.payment__table-total
|
if showSubtotal
|
||||||
table
|
div(class=`${prefix}-totals__item`)
|
||||||
tbody
|
div(class=`${prefix}-totals__item-label`) #{subtotalLabel}
|
||||||
tr.payment-amount
|
div(class=`${prefix}-totals__item-amount`) #{subtotal}
|
||||||
td #{__('payment.paper.payment_amount')}
|
|
||||||
td #{paymentReceive.formattedAmount}
|
|
||||||
tr.blanace-due
|
|
||||||
td #{__('payment.paper.balance_due')}
|
|
||||||
td #{paymentReceive.customer.closingBalance}
|
|
||||||
|
|
||||||
div.payment__footer
|
if showTotal
|
||||||
if paymentReceive.statement
|
div(class=`${prefix}-totals__item`)
|
||||||
div.payment__notes
|
div(class=`${prefix}-totals__item-label`) #{totalLabel}
|
||||||
h3 #{__("payment.paper.statement")}
|
div(class=`${prefix}-totals__item-amount`) #{total}
|
||||||
p #{paymentReceive.statement}
|
|
||||||
|
|||||||
@@ -1,77 +1,190 @@
|
|||||||
extends ../PaperTemplateLayout.pug
|
extends ../PaperTemplateLayout.pug
|
||||||
|
|
||||||
block head
|
block head
|
||||||
style
|
- var prefix = 'bc'
|
||||||
if (isRtl)
|
style.
|
||||||
include ../../css/modules/receipt-rtl.css
|
.#{prefix}-root {
|
||||||
else
|
color: #111;
|
||||||
include ../../css/modules/receipt.css
|
padding: 24px 30px;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
|
||||||
|
}
|
||||||
|
.#{prefix}-logo-wrap {
|
||||||
|
font-size: 60px;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-big-title {
|
||||||
|
height: 120px;
|
||||||
|
width: 120px;
|
||||||
|
position: absolute;
|
||||||
|
right: 26px;
|
||||||
|
top: 26px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item__label {
|
||||||
|
min-width: 120px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-terms-item__value {}
|
||||||
|
.#{prefix}-address-section {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-pack: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-address {}
|
||||||
|
.#{prefix}-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
text-align: left;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header {
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
padding: 2px 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header:first-of-type{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header:last-of-type{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__header--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell {
|
||||||
|
border-bottom: 1px solid #F6F6F6;
|
||||||
|
padding: 12px 10px;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell:first-of-type{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell:last-of-type {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: auto;
|
||||||
|
width: 300px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__line {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__line--gray-border {}
|
||||||
|
.#{prefix}-totals__line--dark-border {}
|
||||||
|
.#{prefix}-totals__line__label {
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
.#{prefix}-totals__line__amount {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.#{prefix}-statement {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.#{prefix}-statement__label {}
|
||||||
|
.#{prefix}-statement__value {}
|
||||||
|
|
||||||
block content
|
block content
|
||||||
div.receipt
|
//- block head
|
||||||
div.receipt__header
|
div(class=`${prefix}-root`, style=`--invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor};`)
|
||||||
div.paper
|
|
||||||
h1.title #{__("receipt.paper.receipt")}
|
//- Title and company logo
|
||||||
span.receiptNumber #{saleReceipt.receiptNumber}
|
h1(class=`${prefix}-big-title`) Receipt
|
||||||
|
|
||||||
div.organization
|
if showCompanyLogo
|
||||||
h3.title #{organizationName}
|
div(class=`${prefix}-logo-wrap`)
|
||||||
|
img(src=companyLogo alt=`Company Logo`)
|
||||||
|
|
||||||
div.receipt__receipt-amount
|
//- Terms List
|
||||||
div.label #{__('receipt.paper.receipt_amount')}
|
div(class=`${prefix}-terms-list`)
|
||||||
div.amount #{saleReceipt.formattedAmount}
|
if showReceiptNumber
|
||||||
|
div(class=`${prefix}-terms-item`)
|
||||||
|
span(class=`${prefix}-terms-item__label`)= receiptNumberLabel
|
||||||
|
span(class=`${prefix}-terms-item__value`)= receiptNumber
|
||||||
|
if showReceiptDate
|
||||||
|
div(class=`${prefix}-terms-item`)
|
||||||
|
span(class=`${prefix}-terms-item__label`)= receiptDateLabel
|
||||||
|
span(class=`${prefix}-terms-item__value`)= receiptDate
|
||||||
|
|
||||||
div.receipt__meta
|
//- Address Section
|
||||||
div.receipt__meta-item.receipt__meta-item--billed-to
|
div(class=`${prefix}-address-section`)
|
||||||
span.label #{__("receipt.paper.billed_to")}
|
if showBilledFromAddress
|
||||||
span.value #{saleReceipt.customer.displayName}
|
div(class=`${prefix}-address`)
|
||||||
|
strong= companyName
|
||||||
|
each addressLine in billedFromAddress
|
||||||
|
div= addressLine
|
||||||
|
|
||||||
div.receipt__meta-item.receipt__meta-item--invoice-date
|
if showBilledToAddress
|
||||||
span.label #{__("receipt.paper.receipt_date")}
|
div(class=`${prefix}-address`)
|
||||||
span.value #{saleReceipt.formattedReceiptDate}
|
strong= billedToLabel
|
||||||
|
each addressLine in billedToAddress
|
||||||
|
div= addressLine
|
||||||
|
|
||||||
if saleReceipt.receiptNumber
|
//- Table Section
|
||||||
div.receipt__meta-item.receipt__meta-item--invoice-number
|
table(class=`${prefix}-table`)
|
||||||
span.label #{__("receipt.paper.receipt_number")}
|
thead(class=`${prefix}-table__header`)
|
||||||
span.value #{saleReceipt.receiptNumber}
|
tr
|
||||||
|
th(class=`${prefix}-table__header`) Item
|
||||||
|
th(class=`${prefix}-table__header`) Description
|
||||||
|
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Rate
|
||||||
|
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Total
|
||||||
|
tbody
|
||||||
|
each line in lines
|
||||||
|
tr(class=`${prefix}-table__row`)
|
||||||
|
td(class=`${prefix}-table__column`)= line.item
|
||||||
|
td(class=`${prefix}-table__column`)= line.description
|
||||||
|
td(class=`${prefix}-table__column ${prefix}-table__column--right`)= line.rate
|
||||||
|
td(class=`${prefix}-table__column ${prefix}-table__column--right`)= line.total
|
||||||
|
|
||||||
div.receipt__table
|
//- Totals Section
|
||||||
table
|
div(class=`${prefix}-totals`)
|
||||||
thead
|
if showSubtotal
|
||||||
tr
|
div(class=`${prefix}-totals__line #{prefix}-totals__line--gray-border`)
|
||||||
th.item #{__("item_entry.paper.item_name")}
|
span(class=`${prefix}-totals__line__label`)= subtotalLabel
|
||||||
th.rate #{__("item_entry.paper.rate")}
|
span(class=`${prefix}-totals__line__amount`)= subtotal
|
||||||
th.quantity #{__("item_entry.paper.quantity")}
|
if showTotal
|
||||||
th.total #{__("item_entry.paper.total")}
|
div(class=`${prefix}-totals__line #{prefix}-totals__line--dark-border`)
|
||||||
tbody
|
span(class=`${prefix}-totals__line__label`)= totalLabel
|
||||||
each entry in saleReceipt.entries
|
span(class=`${prefix}-totals__line__amount`)= total
|
||||||
tr
|
|
||||||
td.item=entry.item.name
|
|
||||||
td.rate=entry.rate
|
|
||||||
td.quantity=entry.quantity
|
|
||||||
td.total=entry.amount
|
|
||||||
|
|
||||||
div.receipt__table-after
|
|
||||||
div.receipt__table-total
|
|
||||||
table
|
|
||||||
tbody
|
|
||||||
tr.total
|
|
||||||
td #{__('receipt.paper.total')}
|
|
||||||
td #{saleReceipt.formattedAmount}
|
|
||||||
tr.payment-amount
|
|
||||||
td #{__('receipt.paper.payment_amount')}
|
|
||||||
td #{saleReceipt.formattedAmount}
|
|
||||||
tr.blanace-due
|
|
||||||
td #{__('receipt.paper.balance_due')}
|
|
||||||
td #{'$0'}
|
|
||||||
|
|
||||||
div.receipt__footer
|
//- Customer Note Section
|
||||||
if saleReceipt.statement
|
if showCustomerNote
|
||||||
div.receipt__conditions
|
div(class=`${prefix}-statement`)
|
||||||
h3 #{__("receipt.paper.statement")}
|
span(class=`${prefix}-statement__label`)= customerNoteLabel
|
||||||
p #{saleReceipt.statement}
|
p(class=`${prefix}-statement__value`)= customerNote
|
||||||
|
|
||||||
if saleReceipt.receiptMessage
|
//- Terms & Conditions Section
|
||||||
div.receipt__notes
|
if showTermsConditions
|
||||||
h3 #{__("receipt.paper.notes")}
|
div(class=`${prefix}-statement`)
|
||||||
p #{saleReceipt.receiptMessage}
|
span(class=`${prefix}-statement__label`)= termsConditionsLabel
|
||||||
|
p(class=`${prefix}-statement__value`)= termsConditions
|
||||||
|
|||||||
@@ -258,3 +258,49 @@ export type ICreditNoteGLCommonEntry = Pick<
|
|||||||
| 'debit'
|
| 'debit'
|
||||||
| 'branchId'
|
| 'branchId'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export interface CreditNotePdfTemplateAttributes {
|
||||||
|
primaryColor: string;
|
||||||
|
secondaryColor: string;
|
||||||
|
showCompanyLogo: boolean;
|
||||||
|
companyLogo: string;
|
||||||
|
companyName: string;
|
||||||
|
|
||||||
|
billedToAddress: string[];
|
||||||
|
billedFromAddress: string[];
|
||||||
|
showBilledToAddress: boolean;
|
||||||
|
showBilledFromAddress: boolean;
|
||||||
|
billedToLabel: string;
|
||||||
|
|
||||||
|
total: string;
|
||||||
|
totalLabel: string;
|
||||||
|
showTotal: boolean;
|
||||||
|
|
||||||
|
subtotal: string;
|
||||||
|
subtotalLabel: string;
|
||||||
|
showSubtotal: boolean;
|
||||||
|
|
||||||
|
showCustomerNote: boolean;
|
||||||
|
customerNote: string;
|
||||||
|
customerNoteLabel: string;
|
||||||
|
|
||||||
|
showTermsConditions: boolean;
|
||||||
|
termsConditions: string;
|
||||||
|
termsConditionsLabel: string;
|
||||||
|
|
||||||
|
lines: Array<{
|
||||||
|
item: string;
|
||||||
|
description: string;
|
||||||
|
rate: string;
|
||||||
|
quantity: string;
|
||||||
|
total: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
showCreditNoteNumber: boolean;
|
||||||
|
creditNoteNumberLabel: string;
|
||||||
|
creditNoteNumebr: string;
|
||||||
|
|
||||||
|
creditNoteDate: string;
|
||||||
|
showCreditNoteDate: boolean;
|
||||||
|
creditNoteDateLabel: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export interface IPaymentReceived {
|
|||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
localAmount?: number;
|
localAmount?: number;
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
pdfTemplateId?: number;
|
||||||
}
|
}
|
||||||
export interface IPaymentReceivedCreateDTO {
|
export interface IPaymentReceivedCreateDTO {
|
||||||
customerId: number;
|
customerId: number;
|
||||||
@@ -185,3 +186,70 @@ export interface PaymentReceiveMailPresendEvent {
|
|||||||
paymentReceiveId: number;
|
paymentReceiveId: number;
|
||||||
messageOptions: PaymentReceiveMailOptsDTO;
|
messageOptions: PaymentReceiveMailOptsDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PaymentReceivedPdfLineItem {
|
||||||
|
item: string;
|
||||||
|
description: string;
|
||||||
|
rate: string;
|
||||||
|
quantity: string;
|
||||||
|
total: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaymentReceivedPdfTax {
|
||||||
|
label: string;
|
||||||
|
amount: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaymentReceivedPdfTemplateAttributes {
|
||||||
|
primaryColor: string;
|
||||||
|
secondaryColor: string;
|
||||||
|
companyName: string;
|
||||||
|
|
||||||
|
showCompanyLogo: boolean;
|
||||||
|
companyLogo: string;
|
||||||
|
|
||||||
|
dueDateLabel: string;
|
||||||
|
showDueDate: boolean;
|
||||||
|
|
||||||
|
dateIssueLabel: string;
|
||||||
|
showDateIssue: boolean;
|
||||||
|
|
||||||
|
invoiceNumberLabel: string;
|
||||||
|
showInvoiceNumber: boolean;
|
||||||
|
|
||||||
|
showBillingToAddress: boolean;
|
||||||
|
showBilledFromAddress: boolean;
|
||||||
|
billedToLabel: string;
|
||||||
|
|
||||||
|
lineItemLabel: string;
|
||||||
|
lineDescriptionLabel: string;
|
||||||
|
lineRateLabel: string;
|
||||||
|
lineTotalLabel: string;
|
||||||
|
|
||||||
|
totalLabel: string;
|
||||||
|
subtotalLabel: string;
|
||||||
|
discountLabel: string;
|
||||||
|
paymentMadeLabel: string;
|
||||||
|
balanceDueLabel: string;
|
||||||
|
|
||||||
|
showTotal: boolean;
|
||||||
|
showSubtotal: boolean;
|
||||||
|
showDiscount: boolean;
|
||||||
|
showTaxes: boolean;
|
||||||
|
showPaymentMade: boolean;
|
||||||
|
showDueAmount: boolean;
|
||||||
|
showBalanceDue: boolean;
|
||||||
|
|
||||||
|
discount: string;
|
||||||
|
|
||||||
|
termsConditionsLabel: string;
|
||||||
|
showTermsConditions: boolean;
|
||||||
|
|
||||||
|
lines: PaymentReceivedPdfLineItem[];
|
||||||
|
taxes: PaymentReceivedPdfTax[];
|
||||||
|
|
||||||
|
statementLabel: string;
|
||||||
|
showStatement: boolean;
|
||||||
|
billedToAddress: string[];
|
||||||
|
billedFromAddress: string[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -143,3 +143,4 @@ export interface ISaleEstimateMailPresendEvent {
|
|||||||
saleEstimateId: number;
|
saleEstimateId: number;
|
||||||
messageOptions: SaleEstimateMailOptionsDTO;
|
messageOptions: SaleEstimateMailOptionsDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export default class CreateCreditNote extends BaseCreditNotes {
|
|||||||
creditNoteDTO.entries
|
creditNoteDTO.entries
|
||||||
);
|
);
|
||||||
// Transformes the given DTO to storage layer data.
|
// Transformes the given DTO to storage layer data.
|
||||||
const creditNoteModel = this.transformCreateEditDTOToModel(
|
const creditNoteModel = await this.transformCreateEditDTOToModel(
|
||||||
tenantId,
|
tenantId,
|
||||||
creditNoteDTO,
|
creditNoteDTO,
|
||||||
customer.currencyCode
|
customer.currencyCode
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Inject } from "typedi";
|
||||||
|
import { GetPdfTemplate } from "../PdfTemplate/GetPdfTemplate";
|
||||||
|
import { defaultCreditNoteBrandingAttributes } from "./constants";
|
||||||
|
import { mergePdfTemplateWithDefaultAttributes } from "../Sales/Invoices/utils";
|
||||||
|
|
||||||
|
export class CreditNoteBrandingTemplate {
|
||||||
|
@Inject()
|
||||||
|
private getPdfTemplateService: GetPdfTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the credit note branding template.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} templateId
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
public async getCreditNoteBrandingTemplate(tenantId: number, templateId: number) {
|
||||||
|
const template = await this.getPdfTemplateService.getPdfTemplate(
|
||||||
|
tenantId,
|
||||||
|
templateId
|
||||||
|
);
|
||||||
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
|
template.attributes,
|
||||||
|
defaultCreditNoteBrandingAttributes
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...template,
|
||||||
|
attributes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { Service, Inject } from 'typedi';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
|
import composeAsync from 'async/compose';
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { ERRORS } from './constants';
|
import { ERRORS } from './constants';
|
||||||
@@ -16,6 +17,7 @@ import AutoIncrementOrdersService from '@/services/Sales/AutoIncrementOrdersServ
|
|||||||
import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
||||||
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||||
import { assocItemEntriesDefaultIndex } from '../Items/utils';
|
import { assocItemEntriesDefaultIndex } from '../Items/utils';
|
||||||
|
import { BrandingTemplateDTOTransformer } from '../PdfTemplate/BrandingTemplateDTOTransformer';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class BaseCreditNotes {
|
export default class BaseCreditNotes {
|
||||||
@@ -34,17 +36,20 @@ export default class BaseCreditNotes {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private warehouseDTOTransform: WarehouseTransactionDTOTransform;
|
private warehouseDTOTransform: WarehouseTransactionDTOTransform;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private brandingTemplatesTransformer: BrandingTemplateDTOTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes the credit/edit DTO to model.
|
* Transformes the credit/edit DTO to model.
|
||||||
* @param {ICreditNoteNewDTO | ICreditNoteEditDTO} creditNoteDTO
|
* @param {ICreditNoteNewDTO | ICreditNoteEditDTO} creditNoteDTO
|
||||||
* @param {string} customerCurrencyCode -
|
* @param {string} customerCurrencyCode -
|
||||||
*/
|
*/
|
||||||
protected transformCreateEditDTOToModel = (
|
protected transformCreateEditDTOToModel = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
creditNoteDTO: ICreditNoteNewDTO | ICreditNoteEditDTO,
|
creditNoteDTO: ICreditNoteNewDTO | ICreditNoteEditDTO,
|
||||||
customerCurrencyCode: string,
|
customerCurrencyCode: string,
|
||||||
oldCreditNote?: ICreditNote
|
oldCreditNote?: ICreditNote
|
||||||
): ICreditNote => {
|
): Promise<ICreditNote> => {
|
||||||
// Retrieve the total amount of the given items entries.
|
// Retrieve the total amount of the given items entries.
|
||||||
const amount = this.itemsEntriesService.getTotalItemsEntries(
|
const amount = this.itemsEntriesService.getTotalItemsEntries(
|
||||||
creditNoteDTO.entries
|
creditNoteDTO.entries
|
||||||
@@ -83,10 +88,18 @@ export default class BaseCreditNotes {
|
|||||||
refundedAmount: 0,
|
refundedAmount: 0,
|
||||||
invoicesAmount: 0,
|
invoicesAmount: 0,
|
||||||
};
|
};
|
||||||
|
const initialAsyncDTO = await composeAsync(
|
||||||
|
// Assigns the default branding template id to the invoice DTO.
|
||||||
|
this.brandingTemplatesTransformer.assocDefaultBrandingTemplate(
|
||||||
|
tenantId,
|
||||||
|
'CreditNote'
|
||||||
|
)
|
||||||
|
)(initialDTO);
|
||||||
|
|
||||||
return R.compose(
|
return R.compose(
|
||||||
this.branchDTOTransform.transformDTO<ICreditNote>(tenantId),
|
this.branchDTOTransform.transformDTO<ICreditNote>(tenantId),
|
||||||
this.warehouseDTOTransform.transformDTO<ICreditNote>(tenantId)
|
this.warehouseDTOTransform.transformDTO<ICreditNote>(tenantId)
|
||||||
)(initialDTO);
|
)(initialAsyncDTO);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default class EditCreditNote extends BaseCreditNotes {
|
|||||||
creditNoteEditDTO.entries
|
creditNoteEditDTO.entries
|
||||||
);
|
);
|
||||||
// Transformes the given DTO to storage layer data.
|
// Transformes the given DTO to storage layer data.
|
||||||
const creditNoteModel = this.transformCreateEditDTOToModel(
|
const creditNoteModel = await this.transformCreateEditDTOToModel(
|
||||||
tenantId,
|
tenantId,
|
||||||
creditNoteEditDTO,
|
creditNoteEditDTO,
|
||||||
customer.currencyCode,
|
customer.currencyCode,
|
||||||
|
|||||||
@@ -2,9 +2,16 @@ import { Inject, Service } from 'typedi';
|
|||||||
import { ChromiumlyTenancy } from '../ChromiumlyTenancy/ChromiumlyTenancy';
|
import { ChromiumlyTenancy } from '../ChromiumlyTenancy/ChromiumlyTenancy';
|
||||||
import { TemplateInjectable } from '../TemplateInjectable/TemplateInjectable';
|
import { TemplateInjectable } from '../TemplateInjectable/TemplateInjectable';
|
||||||
import GetCreditNote from './GetCreditNote';
|
import GetCreditNote from './GetCreditNote';
|
||||||
|
import { CreditNoteBrandingTemplate } from './CreditNoteBrandingTemplate';
|
||||||
|
import { CreditNotePdfTemplateAttributes } from '@/interfaces';
|
||||||
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
|
import { transformCreditNoteToPdfTemplate } from './utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class GetCreditNotePdf {
|
export default class GetCreditNotePdf {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private chromiumlyTenancy: ChromiumlyTenancy;
|
private chromiumlyTenancy: ChromiumlyTenancy;
|
||||||
|
|
||||||
@@ -14,25 +21,62 @@ export default class GetCreditNotePdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getCreditNoteService: GetCreditNote;
|
private getCreditNoteService: GetCreditNote;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private creditNoteBrandingTemplate: CreditNoteBrandingTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice pdf content.
|
* Retrieves sale invoice pdf content.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} creditNoteId - Credit note id.
|
* @param {number} creditNoteId - Credit note id.
|
||||||
*/
|
*/
|
||||||
public async getCreditNotePdf(tenantId: number, creditNoteId: number) {
|
public async getCreditNotePdf(tenantId: number, creditNoteId: number) {
|
||||||
|
const brandingAttributes = await this.getCreditNoteBrandingAttributes(
|
||||||
|
tenantId,
|
||||||
|
creditNoteId
|
||||||
|
);
|
||||||
|
console.log(brandingAttributes, 'brandingAttributes');
|
||||||
|
|
||||||
|
const htmlContent = await this.templateInjectable.render(
|
||||||
|
tenantId,
|
||||||
|
'modules/credit-note-standard',
|
||||||
|
brandingAttributes
|
||||||
|
);
|
||||||
|
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves credit note branding attributes.
|
||||||
|
* @param {number} tenantId - The ID of the tenant.
|
||||||
|
* @param {number} creditNoteId - The ID of the credit note.
|
||||||
|
* @returns {Promise<CreditNotePdfTemplateAttributes>} The credit note branding attributes.
|
||||||
|
*/
|
||||||
|
public async getCreditNoteBrandingAttributes(
|
||||||
|
tenantId: number,
|
||||||
|
creditNoteId: number
|
||||||
|
): Promise<CreditNotePdfTemplateAttributes> {
|
||||||
|
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||||
const creditNote = await this.getCreditNoteService.getCreditNote(
|
const creditNote = await this.getCreditNoteService.getCreditNote(
|
||||||
tenantId,
|
tenantId,
|
||||||
creditNoteId
|
creditNoteId
|
||||||
);
|
);
|
||||||
const htmlContent = await this.templateInjectable.render(
|
// Retrieve the invoice template id of not found get the default template id.
|
||||||
tenantId,
|
const templateId =
|
||||||
'modules/credit-note-standard',
|
creditNote.pdfTemplateId ??
|
||||||
{
|
(
|
||||||
creditNote,
|
await PdfTemplate.query().findOne({
|
||||||
}
|
resource: 'CreditNote',
|
||||||
);
|
default: true,
|
||||||
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent, {
|
})
|
||||||
margins: { top: 0, bottom: 0, left: 0, right: 0 },
|
)?.id;
|
||||||
});
|
// Retrieves the credit note branding template.
|
||||||
|
const brandingTemplate =
|
||||||
|
await this.creditNoteBrandingTemplate.getCreditNoteBrandingTemplate(
|
||||||
|
tenantId,
|
||||||
|
templateId
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...brandingTemplate.attributes,
|
||||||
|
...transformCreditNoteToPdfTemplate(creditNote),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const ERRORS = {
|
|||||||
'CREDIT_NOTE_APPLY_TO_INVOICES_NOT_FOUND',
|
'CREDIT_NOTE_APPLY_TO_INVOICES_NOT_FOUND',
|
||||||
CREDIT_NOTE_HAS_REFUNDS_TRANSACTIONS: 'CREDIT_NOTE_HAS_REFUNDS_TRANSACTIONS',
|
CREDIT_NOTE_HAS_REFUNDS_TRANSACTIONS: 'CREDIT_NOTE_HAS_REFUNDS_TRANSACTIONS',
|
||||||
CREDIT_NOTE_HAS_APPLIED_INVOICES: 'CREDIT_NOTE_HAS_APPLIED_INVOICES',
|
CREDIT_NOTE_HAS_APPLIED_INVOICES: 'CREDIT_NOTE_HAS_APPLIED_INVOICES',
|
||||||
CUSTOMER_HAS_LINKED_CREDIT_NOTES: 'CUSTOMER_HAS_LINKED_CREDIT_NOTES'
|
CUSTOMER_HAS_LINKED_CREDIT_NOTES: 'CUSTOMER_HAS_LINKED_CREDIT_NOTES',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_VIEW_COLUMNS = [];
|
export const DEFAULT_VIEW_COLUMNS = [];
|
||||||
@@ -66,3 +66,72 @@ export const DEFAULT_VIEWS = [
|
|||||||
columns: DEFAULT_VIEW_COLUMNS,
|
columns: DEFAULT_VIEW_COLUMNS,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const defaultCreditNoteBrandingAttributes = {
|
||||||
|
primaryColor: '',
|
||||||
|
secondaryColor: '',
|
||||||
|
showCompanyLogo: true,
|
||||||
|
companyLogo: '',
|
||||||
|
companyName: 'Bigcapital Technology, Inc.',
|
||||||
|
|
||||||
|
// Address
|
||||||
|
billedToAddress: [
|
||||||
|
'Bigcapital Technology, Inc.',
|
||||||
|
'131 Continental Dr Suite 305 Newark,',
|
||||||
|
'Delaware 19713',
|
||||||
|
'United States',
|
||||||
|
'+1 762-339-5634',
|
||||||
|
'ahmed@bigcapital.app',
|
||||||
|
],
|
||||||
|
billedFromAddress: [
|
||||||
|
'131 Continental Dr Suite 305 Newark,',
|
||||||
|
'Delaware 19713',
|
||||||
|
'United States',
|
||||||
|
'+1 762-339-5634',
|
||||||
|
'ahmed@bigcapital.app',
|
||||||
|
],
|
||||||
|
showBilledToAddress: true,
|
||||||
|
showBilledFromAddress: true,
|
||||||
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
|
// Total
|
||||||
|
total: '$1000.00',
|
||||||
|
totalLabel: 'Total',
|
||||||
|
showTotal: true,
|
||||||
|
|
||||||
|
// Subtotal
|
||||||
|
subtotal: '1000/00',
|
||||||
|
subtotalLabel: 'Subtotal',
|
||||||
|
showSubtotal: true,
|
||||||
|
|
||||||
|
// Customer note
|
||||||
|
showCustomerNote: true,
|
||||||
|
customerNote:
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||||
|
customerNoteLabel: 'Customer Note',
|
||||||
|
|
||||||
|
// Terms & conditions
|
||||||
|
showTermsConditions: true,
|
||||||
|
termsConditions:
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||||
|
termsConditionsLabel: 'Terms & Conditions',
|
||||||
|
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
item: 'Simply dummy text',
|
||||||
|
description: 'Simply dummy text of the printing and typesetting',
|
||||||
|
rate: '1',
|
||||||
|
quantity: '1000',
|
||||||
|
total: '$1000.00',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// Credit note number.
|
||||||
|
showCreditNoteNumber: true,
|
||||||
|
creditNoteNumberLabel: 'Credit Note Number',
|
||||||
|
creditNoteNumebr: '346D3D40-0001',
|
||||||
|
|
||||||
|
// Credit note date.
|
||||||
|
creditNoteDate: 'September 3, 2024',
|
||||||
|
showCreditNoteDate: true,
|
||||||
|
creditNoteDateLabel: 'Credit Note Date',
|
||||||
|
};
|
||||||
|
|||||||
9
packages/server/src/services/CreditNotes/utils.ts
Normal file
9
packages/server/src/services/CreditNotes/utils.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { CreditNotePdfTemplateAttributes } from "@/interfaces";
|
||||||
|
import CreditNote from "@/models/CreditNote";
|
||||||
|
|
||||||
|
|
||||||
|
export const transformCreditNoteToPdfTemplate = (creditNote: CreditNote): Partial<CreditNotePdfTemplateAttributes> {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import HasTenancyService from '../Tenancy/TenancyService';
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class BrandingTemplateDTOTransformer {
|
export class BrandingTemplateDTOTransformer {
|
||||||
@@ -9,31 +10,28 @@ export class BrandingTemplateDTOTransformer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Associates the default branding template id.
|
* Associates the default branding template id.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {string} resource
|
* @param {string} resource
|
||||||
* @param {Record<string, any>} object
|
* @param {Record<string, any>} object
|
||||||
* @param {string} attributeName
|
* @param {string} attributeName
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public assocDefaultBrandingTemplate = (
|
public assocDefaultBrandingTemplate =
|
||||||
tenantId: number,
|
(tenantId: number, resource: string) =>
|
||||||
resource: string,
|
async (object: Record<string, any>) => {
|
||||||
) => async (object: Record<string, any>) => {
|
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||||
const { PdfTemplate } = this.tenancy.models(tenantId);
|
const attributeName = 'pdfTemplateId';
|
||||||
const attributeName = 'pdfTemplateId';
|
|
||||||
|
|
||||||
const defaultTemplate = await PdfTemplate.query().findOne({
|
const defaultTemplate = await PdfTemplate.query().findOne({
|
||||||
resource,
|
resource,
|
||||||
default: true,
|
default: true,
|
||||||
});
|
});
|
||||||
console.log(defaultTemplate);
|
if (!defaultTemplate || !isEmpty(object[attributeName])) {
|
||||||
|
return object;
|
||||||
if (!defaultTemplate) {
|
}
|
||||||
return object;
|
return {
|
||||||
}
|
...object,
|
||||||
return {
|
[attributeName]: defaultTemplate.id,
|
||||||
...object,
|
};
|
||||||
[attributeName]: defaultTemplate.id,
|
|
||||||
};
|
};
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { omit, sumBy } from 'lodash';
|
import { omit, sumBy } from 'lodash';
|
||||||
|
import composeAsync from 'async/compose';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { ICustomer, ISaleEstimate, ISaleEstimateDTO } from '@/interfaces';
|
import { ICustomer, ISaleEstimate, ISaleEstimateDTO } from '@/interfaces';
|
||||||
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
||||||
@@ -10,6 +11,7 @@ import { formatDateFields } from '@/utils';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { SaleEstimateIncrement } from './SaleEstimateIncrement';
|
import { SaleEstimateIncrement } from './SaleEstimateIncrement';
|
||||||
import { assocItemEntriesDefaultIndex } from '@/services/Items/utils';
|
import { assocItemEntriesDefaultIndex } from '@/services/Items/utils';
|
||||||
|
import { BrandingTemplateDTOTransformer } from '@/services/PdfTemplate/BrandingTemplateDTOTransformer';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleEstimateDTOTransformer {
|
export class SaleEstimateDTOTransformer {
|
||||||
@@ -28,6 +30,9 @@ export class SaleEstimateDTOTransformer {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private estimateIncrement: SaleEstimateIncrement;
|
private estimateIncrement: SaleEstimateIncrement;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private brandingTemplatesTransformer: BrandingTemplateDTOTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform create DTO object ot model object.
|
* Transform create DTO object ot model object.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -81,10 +86,18 @@ export class SaleEstimateDTOTransformer {
|
|||||||
deliveredAt: moment().toMySqlDateTime(),
|
deliveredAt: moment().toMySqlDateTime(),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
const initialAsyncDTO = await composeAsync(
|
||||||
|
// Assigns the default branding template id to the invoice DTO.
|
||||||
|
this.brandingTemplatesTransformer.assocDefaultBrandingTemplate(
|
||||||
|
tenantId,
|
||||||
|
'SaleEstimate'
|
||||||
|
)
|
||||||
|
)(initialDTO);
|
||||||
|
|
||||||
return R.compose(
|
return R.compose(
|
||||||
this.branchDTOTransform.transformDTO<ISaleEstimate>(tenantId),
|
this.branchDTOTransform.transformDTO<ISaleEstimate>(tenantId),
|
||||||
this.warehouseDTOTransform.transformDTO<ISaleEstimate>(tenantId)
|
this.warehouseDTOTransform.transformDTO<ISaleEstimate>(tenantId)
|
||||||
)(initialDTO);
|
)(initialAsyncDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,9 +2,16 @@ import { Inject, Service } from 'typedi';
|
|||||||
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
||||||
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
||||||
import { GetSaleEstimate } from './GetSaleEstimate';
|
import { GetSaleEstimate } from './GetSaleEstimate';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import { SaleEstimatePdfTemplate } from '../Invoices/SaleEstimatePdfTemplate';
|
||||||
|
import { transformEstimateToPdfTemplate } from './utils';
|
||||||
|
import { EstimatePdfBrandingAttributes } from './constants';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleEstimatesPdf {
|
export class SaleEstimatesPdf {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private chromiumlyTenancy: ChromiumlyTenancy;
|
private chromiumlyTenancy: ChromiumlyTenancy;
|
||||||
|
|
||||||
@@ -14,25 +21,58 @@ export class SaleEstimatesPdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getSaleEstimate: GetSaleEstimate;
|
private getSaleEstimate: GetSaleEstimate;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private estimatePdfTemplate: SaleEstimatePdfTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice pdf content.
|
* Retrieve sale invoice pdf content.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @param {ISaleInvoice} saleInvoice -
|
* @param {ISaleInvoice} saleInvoice -
|
||||||
*/
|
*/
|
||||||
public async getSaleEstimatePdf(tenantId: number, saleEstimateId: number) {
|
public async getSaleEstimatePdf(tenantId: number, saleEstimateId: number) {
|
||||||
const saleEstimate = await this.getSaleEstimate.getEstimate(
|
const brandingAttributes = await this.getEstimateBrandingAttributes(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleEstimateId
|
saleEstimateId
|
||||||
);
|
);
|
||||||
const htmlContent = await this.templateInjectable.render(
|
const htmlContent = await this.templateInjectable.render(
|
||||||
tenantId,
|
tenantId,
|
||||||
'modules/estimate-regular',
|
'modules/estimate-regular',
|
||||||
{
|
brandingAttributes
|
||||||
saleEstimate,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent, {
|
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent);
|
||||||
margins: { top: 0, bottom: 0, left: 0, right: 0 },
|
}
|
||||||
});
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} estimateId
|
||||||
|
*/
|
||||||
|
async getEstimateBrandingAttributes(
|
||||||
|
tenantId: number,
|
||||||
|
estimateId: number
|
||||||
|
): Promise<EstimatePdfBrandingAttributes> {
|
||||||
|
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||||
|
const saleEstimate = await this.getSaleEstimate.getEstimate(
|
||||||
|
tenantId,
|
||||||
|
estimateId
|
||||||
|
);
|
||||||
|
// Retrieve the invoice template id of not found get the default template id.
|
||||||
|
const templateId =
|
||||||
|
saleEstimate.pdfTemplateId ??
|
||||||
|
(
|
||||||
|
await PdfTemplate.query().findOne({
|
||||||
|
resource: 'SaleEstimate',
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
)?.id;
|
||||||
|
const brandingTemplate =
|
||||||
|
await this.estimatePdfTemplate.getEstimatePdfTemplate(
|
||||||
|
tenantId,
|
||||||
|
templateId
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...brandingTemplate.attributes,
|
||||||
|
...transformEstimateToPdfTemplate(saleEstimate),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,3 +173,122 @@ export const SaleEstimatesSampleData = [
|
|||||||
'Line Description': 'Qui suscipit ducimus qui qui.',
|
'Line Description': 'Qui suscipit ducimus qui qui.',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const defaultEstimatePdfBrandingAttributes = {
|
||||||
|
primaryColor: '#000',
|
||||||
|
secondaryColor: '#000',
|
||||||
|
showCompanyLogo: true,
|
||||||
|
companyLogo: '',
|
||||||
|
companyName: '',
|
||||||
|
|
||||||
|
billedToAddress: [
|
||||||
|
'Bigcapital Technology, Inc.',
|
||||||
|
'131 Continental Dr Suite 305 Newark,',
|
||||||
|
'Delaware 19713',
|
||||||
|
'United States',
|
||||||
|
'+1 762-339-5634',
|
||||||
|
'ahmed@bigcapital.app',
|
||||||
|
],
|
||||||
|
billedFromAddress: [
|
||||||
|
'131 Continental Dr Suite 305 Newark,',
|
||||||
|
'Delaware 19713',
|
||||||
|
'United States',
|
||||||
|
'+1 762-339-5634',
|
||||||
|
'ahmed@bigcapital.app',
|
||||||
|
],
|
||||||
|
showBilledFromAddress: true,
|
||||||
|
showBilledToAddress: true,
|
||||||
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
|
total: '$1000.00',
|
||||||
|
totalLabel: 'Total',
|
||||||
|
showTotal: true,
|
||||||
|
|
||||||
|
subtotal: '1000/00',
|
||||||
|
subtotalLabel: 'Subtotal',
|
||||||
|
showSubtotal: true,
|
||||||
|
|
||||||
|
showCustomerNote: true,
|
||||||
|
customerNote:
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||||
|
customerNoteLabel: 'Customer Note',
|
||||||
|
|
||||||
|
showTermsConditions: true,
|
||||||
|
termsConditions:
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||||
|
termsConditionsLabel: 'Terms & Conditions',
|
||||||
|
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
item: 'Simply dummy text',
|
||||||
|
description: 'Simply dummy text of the printing and typesetting',
|
||||||
|
rate: '1',
|
||||||
|
quantity: '1000',
|
||||||
|
total: '$1000.00',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showEstimateNumber: true,
|
||||||
|
estimateNumberLabel: 'Estimate Number',
|
||||||
|
estimateNumebr: '346D3D40-0001',
|
||||||
|
|
||||||
|
estimateDate: 'September 3, 2024',
|
||||||
|
showEstimateDate: true,
|
||||||
|
estimateDateLabel: 'Estimate Date',
|
||||||
|
|
||||||
|
expirationDateLabel: 'Expiration Date',
|
||||||
|
showExpirationDate: true,
|
||||||
|
expirationDate: 'September 3, 2024',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
interface EstimatePdfBrandingLineItem {
|
||||||
|
item: string;
|
||||||
|
description: string;
|
||||||
|
rate: string;
|
||||||
|
quantity: string;
|
||||||
|
total: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EstimatePdfBrandingAttributes {
|
||||||
|
primaryColor: string;
|
||||||
|
secondaryColor: string;
|
||||||
|
showCompanyLogo: boolean;
|
||||||
|
companyLogo: string;
|
||||||
|
companyName: string;
|
||||||
|
|
||||||
|
billedToAddress: string[];
|
||||||
|
billedFromAddress: string[];
|
||||||
|
showBilledFromAddress: boolean;
|
||||||
|
showBilledToAddress: boolean;
|
||||||
|
billedToLabel: string;
|
||||||
|
|
||||||
|
total: string;
|
||||||
|
totalLabel: string;
|
||||||
|
showTotal: boolean;
|
||||||
|
|
||||||
|
subtotal: string;
|
||||||
|
subtotalLabel: string;
|
||||||
|
showSubtotal: boolean;
|
||||||
|
|
||||||
|
showCustomerNote: boolean;
|
||||||
|
customerNote: string;
|
||||||
|
customerNoteLabel: string;
|
||||||
|
|
||||||
|
showTermsConditions: boolean;
|
||||||
|
termsConditions: string;
|
||||||
|
termsConditionsLabel: string;
|
||||||
|
|
||||||
|
lines: EstimatePdfBrandingLineItem[];
|
||||||
|
|
||||||
|
showEstimateNumber: boolean;
|
||||||
|
estimateNumberLabel: string;
|
||||||
|
estimateNumebr: string;
|
||||||
|
|
||||||
|
estimateDate: string;
|
||||||
|
showEstimateDate: boolean;
|
||||||
|
estimateDateLabel: string;
|
||||||
|
|
||||||
|
expirationDateLabel: string;
|
||||||
|
showExpirationDate: boolean;
|
||||||
|
expirationDate: string;
|
||||||
|
}
|
||||||
7
packages/server/src/services/Sales/Estimates/utils.ts
Normal file
7
packages/server/src/services/Sales/Estimates/utils.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { EstimatePdfBrandingAttributes } from './constants';
|
||||||
|
|
||||||
|
export const transformEstimateToPdfTemplate = (
|
||||||
|
estimate
|
||||||
|
): Partial<EstimatePdfBrandingAttributes> => {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { mergePdfTemplateWithDefaultAttributes } from './utils';
|
||||||
|
import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate';
|
||||||
|
import { defaultEstimatePdfBrandingAttributes } from '../Estimates/constants';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class SaleEstimatePdfTemplate {
|
||||||
|
@Inject()
|
||||||
|
private getPdfTemplateService: GetPdfTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the estimate pdf template.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} invoiceTemplateId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getEstimatePdfTemplate(tenantId: number, estimateTemplateId: number) {
|
||||||
|
const template = await this.getPdfTemplateService.getPdfTemplate(
|
||||||
|
tenantId,
|
||||||
|
estimateTemplateId
|
||||||
|
);
|
||||||
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
|
template.attributes,
|
||||||
|
defaultEstimatePdfBrandingAttributes
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...template,
|
||||||
|
attributes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,16 @@ import { Inject, Service } from 'typedi';
|
|||||||
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
||||||
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
||||||
import { GetPaymentReceived } from './GetPaymentReceived';
|
import { GetPaymentReceived } from './GetPaymentReceived';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import { PaymentReceivedBrandingTemplate } from './PaymentReceivedBrandingTemplate';
|
||||||
|
import { transformPaymentReceivedToPdfTemplate } from './utils';
|
||||||
|
import { PaymentReceivedPdfTemplateAttributes } from '@/interfaces';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class GetPaymentReceivedPdf {
|
export default class GetPaymentReceivedPdf {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private chromiumlyTenancy: ChromiumlyTenancy;
|
private chromiumlyTenancy: ChromiumlyTenancy;
|
||||||
|
|
||||||
@@ -14,6 +21,9 @@ export default class GetPaymentReceivedPdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getPaymentService: GetPaymentReceived;
|
private getPaymentService: GetPaymentReceived;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private paymentBrandingTemplateService: PaymentReceivedBrandingTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice pdf content.
|
* Retrieve sale invoice pdf content.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
@@ -24,19 +34,52 @@ export default class GetPaymentReceivedPdf {
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
paymentReceiveId: number
|
paymentReceiveId: number
|
||||||
): Promise<Buffer> {
|
): Promise<Buffer> {
|
||||||
const paymentReceive = await this.getPaymentService.getPaymentReceive(
|
const brandingAttributes = await this.getPaymentBrandingAttributes(
|
||||||
tenantId,
|
tenantId,
|
||||||
paymentReceiveId
|
paymentReceiveId
|
||||||
);
|
);
|
||||||
const htmlContent = await this.templateInjectable.render(
|
const htmlContent = await this.templateInjectable.render(
|
||||||
tenantId,
|
tenantId,
|
||||||
'modules/payment-receive-standard',
|
'modules/payment-receive-standard',
|
||||||
{
|
brandingAttributes
|
||||||
paymentReceive,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent, {
|
// Converts the given html content to pdf document.
|
||||||
margins: { top: 0, bottom: 0, left: 0, right: 0 },
|
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the given payment received branding attributes.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} paymentReceivedId
|
||||||
|
* @returns {Promise<PaymentReceivedPdfTemplateAttributes>}
|
||||||
|
*/
|
||||||
|
async getPaymentBrandingAttributes(
|
||||||
|
tenantId: number,
|
||||||
|
paymentReceivedId: number
|
||||||
|
): Promise<PaymentReceivedPdfTemplateAttributes> {
|
||||||
|
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const paymentReceived = await this.getPaymentService.getPaymentReceive(
|
||||||
|
tenantId,
|
||||||
|
paymentReceivedId
|
||||||
|
);
|
||||||
|
const templateId =
|
||||||
|
paymentReceived?.pdfTemplateId ??
|
||||||
|
(
|
||||||
|
await PdfTemplate.query().findOne({
|
||||||
|
resource: 'PaymentReceive',
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
)?.id;
|
||||||
|
|
||||||
|
const brandingTemplate =
|
||||||
|
await this.paymentBrandingTemplateService.getPaymentReceivedPdfTemplate(
|
||||||
|
tenantId,
|
||||||
|
templateId
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...brandingTemplate.attributes,
|
||||||
|
...transformPaymentReceivedToPdfTemplate(paymentReceived),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate';
|
||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { mergePdfTemplateWithDefaultAttributes } from '../Invoices/utils';
|
||||||
|
import { defaultPaymentReceivedPdfTemplateAttributes } from './constants';
|
||||||
|
import { PdfTemplate } from '@/models/PdfTemplate';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class PaymentReceivedBrandingTemplate {
|
||||||
|
@Inject()
|
||||||
|
private getPdfTemplateService: GetPdfTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the payment received pdf template.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} paymentTemplateId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async getPaymentReceivedPdfTemplate(
|
||||||
|
tenantId: number,
|
||||||
|
paymentTemplateId: number
|
||||||
|
): Promise<PdfTemplate> {
|
||||||
|
const template = await this.getPdfTemplateService.getPdfTemplate(
|
||||||
|
tenantId,
|
||||||
|
paymentTemplateId
|
||||||
|
);
|
||||||
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
|
template.attributes,
|
||||||
|
defaultPaymentReceivedPdfTemplateAttributes
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...template,
|
||||||
|
attributes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { omit, sumBy } from 'lodash';
|
import { omit, sumBy } from 'lodash';
|
||||||
|
import composeAsync from 'async/compose';
|
||||||
import {
|
import {
|
||||||
ICustomer,
|
ICustomer,
|
||||||
IPaymentReceived,
|
IPaymentReceived,
|
||||||
@@ -12,6 +13,7 @@ import { PaymentReceivedIncrement } from './PaymentReceivedIncrement';
|
|||||||
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||||
import { formatDateFields } from '@/utils';
|
import { formatDateFields } from '@/utils';
|
||||||
import { assocItemEntriesDefaultIndex } from '@/services/Items/utils';
|
import { assocItemEntriesDefaultIndex } from '@/services/Items/utils';
|
||||||
|
import { BrandingTemplateDTOTransformer } from '@/services/PdfTemplate/BrandingTemplateDTOTransformer';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class PaymentReceiveDTOTransformer {
|
export class PaymentReceiveDTOTransformer {
|
||||||
@@ -24,6 +26,9 @@ export class PaymentReceiveDTOTransformer {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private branchDTOTransform: BranchTransactionDTOTransform;
|
private branchDTOTransform: BranchTransactionDTOTransform;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private brandingTemplatesTransformer: BrandingTemplateDTOTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes the create payment receive DTO to model object.
|
* Transformes the create payment receive DTO to model object.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -68,8 +73,16 @@ export class PaymentReceiveDTOTransformer {
|
|||||||
exchangeRate: paymentReceiveDTO.exchangeRate || 1,
|
exchangeRate: paymentReceiveDTO.exchangeRate || 1,
|
||||||
entries,
|
entries,
|
||||||
};
|
};
|
||||||
|
const initialAsyncDTO = await composeAsync(
|
||||||
|
// Assigns the default branding template id to the invoice DTO.
|
||||||
|
this.brandingTemplatesTransformer.assocDefaultBrandingTemplate(
|
||||||
|
tenantId,
|
||||||
|
'SaleInvoice'
|
||||||
|
)
|
||||||
|
)(initialDTO);
|
||||||
|
|
||||||
return R.compose(
|
return R.compose(
|
||||||
this.branchDTOTransform.transformDTO<IPaymentReceived>(tenantId)
|
this.branchDTOTransform.transformDTO<IPaymentReceived>(tenantId)
|
||||||
)(initialDTO);
|
)(initialAsyncDTO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,3 +45,53 @@ export const PaymentsReceiveSampleData = [
|
|||||||
'Payment Amount': 850,
|
'Payment Amount': 850,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const defaultPaymentReceivedPdfTemplateAttributes = {
|
||||||
|
primaryColor: '#000',
|
||||||
|
secondaryColor: '#000',
|
||||||
|
showCompanyLogo: true,
|
||||||
|
companyLogo: '',
|
||||||
|
companyName: 'Bigcapital Technology, Inc.',
|
||||||
|
|
||||||
|
billedToAddress: [
|
||||||
|
'Bigcapital Technology, Inc.',
|
||||||
|
'131 Continental Dr Suite 305 Newark,',
|
||||||
|
'Delaware 19713',
|
||||||
|
'United States',
|
||||||
|
'+1 762-339-5634',
|
||||||
|
'ahmed@bigcapital.app',
|
||||||
|
],
|
||||||
|
billedFromAddress: [
|
||||||
|
'131 Continental Dr Suite 305 Newark,',
|
||||||
|
'Delaware 19713',
|
||||||
|
'United States',
|
||||||
|
'+1 762-339-5634',
|
||||||
|
'ahmed@bigcapital.app',
|
||||||
|
],
|
||||||
|
showBilledFromAddress: true,
|
||||||
|
showBillingToAddress: true,
|
||||||
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
|
total: '$1000.00',
|
||||||
|
totalLabel: 'Total',
|
||||||
|
showTotal: true,
|
||||||
|
|
||||||
|
subtotal: '1000/00',
|
||||||
|
subtotalLabel: 'Subtotal',
|
||||||
|
showSubtotal: true,
|
||||||
|
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
invoiceNumber: 'INV-00001',
|
||||||
|
invoiceAmount: '$1000.00',
|
||||||
|
paidAmount: '$1000.00',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showPaymentReceivedNumber: true,
|
||||||
|
paymentReceivedNumberLabel: 'Payment Number',
|
||||||
|
paymentReceivedNumebr: '346D3D40-0001',
|
||||||
|
|
||||||
|
paymentReceivedDate: 'September 3, 2024',
|
||||||
|
showPaymentReceivedDate: true,
|
||||||
|
paymentReceivedDateLabel: 'Payment Date',
|
||||||
|
};
|
||||||
|
|||||||
12
packages/server/src/services/Sales/PaymentReceived/utils.ts
Normal file
12
packages/server/src/services/Sales/PaymentReceived/utils.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import {
|
||||||
|
IPaymentReceived,
|
||||||
|
PaymentReceivedPdfTemplateAttributes,
|
||||||
|
} from '@/interfaces';
|
||||||
|
|
||||||
|
export const transformPaymentReceivedToPdfTemplate = (
|
||||||
|
payment: IPaymentReceived
|
||||||
|
): Partial<PaymentReceivedPdfTemplateAttributes> => {
|
||||||
|
return {
|
||||||
|
// ...payment
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate';
|
||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { defaultSaleReceiptBrandingAttributes } from './constants';
|
||||||
|
import { mergePdfTemplateWithDefaultAttributes } from '../Invoices/utils';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class SaleReceiptBrandingTemplate {
|
||||||
|
@Inject()
|
||||||
|
private getPdfTemplateService: GetPdfTemplate;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the sale receipt branding template.
|
||||||
|
* @param {number} tenantId - The ID of the tenant.
|
||||||
|
* @param {number} templateId - The ID of the PDF template.
|
||||||
|
* @returns {Promise<Object>} The sale receipt branding template with merged attributes.
|
||||||
|
*/
|
||||||
|
public async getSaleReceiptBrandingTemplate(
|
||||||
|
tenantId: number,
|
||||||
|
templateId: number
|
||||||
|
) {
|
||||||
|
const template = await this.getPdfTemplateService.getPdfTemplate(
|
||||||
|
tenantId,
|
||||||
|
templateId
|
||||||
|
);
|
||||||
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
|
template.attributes,
|
||||||
|
defaultSaleReceiptBrandingAttributes
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...template,
|
||||||
|
attributes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import { formatDateFields } from '@/utils';
|
|||||||
import { SaleReceiptIncrement } from './SaleReceiptIncrement';
|
import { SaleReceiptIncrement } from './SaleReceiptIncrement';
|
||||||
import { ItemEntry } from '@/models';
|
import { ItemEntry } from '@/models';
|
||||||
import { assocItemEntriesDefaultIndex } from '@/services/Items/utils';
|
import { assocItemEntriesDefaultIndex } from '@/services/Items/utils';
|
||||||
|
import { BrandingTemplateDTOTransformer } from '@/services/PdfTemplate/BrandingTemplateDTOTransformer';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleReceiptDTOTransformer {
|
export class SaleReceiptDTOTransformer {
|
||||||
@@ -30,6 +31,9 @@ export class SaleReceiptDTOTransformer {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private receiptIncrement: SaleReceiptIncrement;
|
private receiptIncrement: SaleReceiptIncrement;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private brandingTemplatesTransformer: BrandingTemplateDTOTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform create DTO object to model object.
|
* Transform create DTO object to model object.
|
||||||
* @param {ISaleReceiptDTO} saleReceiptDTO -
|
* @param {ISaleReceiptDTO} saleReceiptDTO -
|
||||||
@@ -88,9 +92,17 @@ export class SaleReceiptDTOTransformer {
|
|||||||
}),
|
}),
|
||||||
entries,
|
entries,
|
||||||
};
|
};
|
||||||
|
const initialAsyncDTO = await composeAsync(
|
||||||
|
// Assigns the default branding template id to the invoice DTO.
|
||||||
|
this.brandingTemplatesTransformer.assocDefaultBrandingTemplate(
|
||||||
|
tenantId,
|
||||||
|
'SaleReceipt'
|
||||||
|
)
|
||||||
|
)(initialDTO);
|
||||||
|
|
||||||
return R.compose(
|
return R.compose(
|
||||||
this.branchDTOTransform.transformDTO<ISaleReceipt>(tenantId),
|
this.branchDTOTransform.transformDTO<ISaleReceipt>(tenantId),
|
||||||
this.warehouseDTOTransform.transformDTO<ISaleReceipt>(tenantId)
|
this.warehouseDTOTransform.transformDTO<ISaleReceipt>(tenantId)
|
||||||
)(initialDTO);
|
)(initialAsyncDTO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,15 @@ import { Inject, Service } from 'typedi';
|
|||||||
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
||||||
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
||||||
import { GetSaleReceipt } from './GetSaleReceipt';
|
import { GetSaleReceipt } from './GetSaleReceipt';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import { SaleReceiptBrandingTemplate } from './SaleReceiptBrandingTemplate';
|
||||||
|
import { transformReceiptToBrandingTemplateAttributes } from './utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleReceiptsPdf {
|
export class SaleReceiptsPdf {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private chromiumlyTenancy: ChromiumlyTenancy;
|
private chromiumlyTenancy: ChromiumlyTenancy;
|
||||||
|
|
||||||
@@ -14,26 +20,64 @@ export class SaleReceiptsPdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getSaleReceiptService: GetSaleReceipt;
|
private getSaleReceiptService: GetSaleReceipt;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private saleReceiptBrandingTemplate: SaleReceiptBrandingTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves sale invoice pdf content.
|
* Retrieves sale invoice pdf content.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @param {number} saleInvoiceId -
|
* @param {number} saleInvoiceId -
|
||||||
* @returns {Promise<Buffer>}
|
* @returns {Promise<Buffer>}
|
||||||
*/
|
*/
|
||||||
public async saleReceiptPdf(tenantId: number, saleReceiptId: number) {
|
public async saleReceiptPdf(tenantId: number, saleReceiptId: number) {
|
||||||
const saleReceipt = await this.getSaleReceiptService.getSaleReceipt(
|
const brandingAttributes = await this.getReceiptBrandingAttributes(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleReceiptId
|
saleReceiptId
|
||||||
);
|
);
|
||||||
|
console.log(brandingAttributes, 'attributes');
|
||||||
|
|
||||||
const htmlContent = await this.templateInjectable.render(
|
const htmlContent = await this.templateInjectable.render(
|
||||||
tenantId,
|
tenantId,
|
||||||
'modules/receipt-regular',
|
'modules/receipt-regular',
|
||||||
{
|
brandingAttributes
|
||||||
saleReceipt,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent, {
|
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent);
|
||||||
margins: { top: 0, bottom: 0, left: 0, right: 0 },
|
}
|
||||||
});
|
|
||||||
|
/**
|
||||||
|
* Retrieves receipt branding attributes.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number] receiptId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async getReceiptBrandingAttributes(
|
||||||
|
tenantId: number,
|
||||||
|
receiptId: number
|
||||||
|
) {
|
||||||
|
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const saleReceipt = await this.getSaleReceiptService.getSaleReceipt(
|
||||||
|
tenantId,
|
||||||
|
receiptId
|
||||||
|
);
|
||||||
|
// Retrieve the invoice template id of not found get the default template id.
|
||||||
|
const templateId =
|
||||||
|
saleReceipt.pdfTemplateId ??
|
||||||
|
(
|
||||||
|
await PdfTemplate.query().findOne({
|
||||||
|
resource: 'SaleReceipt',
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
)?.id;
|
||||||
|
// Retrieves the receipt branding template.
|
||||||
|
const brandingTemplate =
|
||||||
|
await this.saleReceiptBrandingTemplate.getSaleReceiptBrandingTemplate(
|
||||||
|
tenantId,
|
||||||
|
templateId
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...brandingTemplate.attributes,
|
||||||
|
...transformReceiptToBrandingTemplateAttributes(saleReceipt),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const ERRORS = {
|
|||||||
SALE_RECEIPT_IS_ALREADY_CLOSED: 'SALE_RECEIPT_IS_ALREADY_CLOSED',
|
SALE_RECEIPT_IS_ALREADY_CLOSED: 'SALE_RECEIPT_IS_ALREADY_CLOSED',
|
||||||
SALE_RECEIPT_NO_IS_REQUIRED: 'SALE_RECEIPT_NO_IS_REQUIRED',
|
SALE_RECEIPT_NO_IS_REQUIRED: 'SALE_RECEIPT_NO_IS_REQUIRED',
|
||||||
CUSTOMER_HAS_SALES_INVOICES: 'CUSTOMER_HAS_SALES_INVOICES',
|
CUSTOMER_HAS_SALES_INVOICES: 'CUSTOMER_HAS_SALES_INVOICES',
|
||||||
NO_INVOICE_CUSTOMER_EMAIL_ADDR: 'NO_INVOICE_CUSTOMER_EMAIL_ADDR'
|
NO_INVOICE_CUSTOMER_EMAIL_ADDR: 'NO_INVOICE_CUSTOMER_EMAIL_ADDR',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_VIEW_COLUMNS = [];
|
export const DEFAULT_VIEW_COLUMNS = [];
|
||||||
@@ -47,22 +47,84 @@ export const DEFAULT_VIEWS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
export const SaleReceiptsSampleData = [
|
export const SaleReceiptsSampleData = [
|
||||||
{
|
{
|
||||||
"Receipt Date": "2023-01-01",
|
'Receipt Date': '2023-01-01',
|
||||||
"Customer": "Randall Kohler",
|
Customer: 'Randall Kohler',
|
||||||
"Deposit Account": "Petty Cash",
|
'Deposit Account': 'Petty Cash',
|
||||||
"Exchange Rate": "",
|
'Exchange Rate': '',
|
||||||
"Receipt Number": "REC-00001",
|
'Receipt Number': 'REC-00001',
|
||||||
"Reference No.": "REF-0001",
|
'Reference No.': 'REF-0001',
|
||||||
"Statement": "Delectus unde aut soluta et accusamus placeat.",
|
Statement: 'Delectus unde aut soluta et accusamus placeat.',
|
||||||
"Receipt Message": "Vitae asperiores dicta.",
|
'Receipt Message': 'Vitae asperiores dicta.',
|
||||||
"Closed": "T",
|
Closed: 'T',
|
||||||
"Item": "Schmitt Group",
|
Item: 'Schmitt Group',
|
||||||
"Quantity": 100,
|
Quantity: 100,
|
||||||
"Rate": 200,
|
Rate: 200,
|
||||||
"Line Description": "Distinctio distinctio sit veritatis consequatur iste quod veritatis."
|
'Line Description':
|
||||||
}
|
'Distinctio distinctio sit veritatis consequatur iste quod veritatis.',
|
||||||
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
|
export const defaultSaleReceiptBrandingAttributes = {
|
||||||
|
primaryColor: '',
|
||||||
|
secondaryColor: '',
|
||||||
|
showCompanyLogo: true,
|
||||||
|
companyLogo: '',
|
||||||
|
companyName: 'Bigcapital Technology, Inc.',
|
||||||
|
|
||||||
|
// # Address
|
||||||
|
billedToAddress: [
|
||||||
|
'Bigcapital Technology, Inc.',
|
||||||
|
'131 Continental Dr Suite 305 Newark,',
|
||||||
|
'Delaware 19713',
|
||||||
|
'United States',
|
||||||
|
'+1 762-339-5634',
|
||||||
|
'ahmed@bigcapital.app',
|
||||||
|
],
|
||||||
|
billedFromAddress: [
|
||||||
|
'131 Continental Dr Suite 305 Newark,',
|
||||||
|
'Delaware 19713',
|
||||||
|
'United States',
|
||||||
|
'+1 762-339-5634',
|
||||||
|
'ahmed@bigcapital.app',
|
||||||
|
],
|
||||||
|
showBilledFromAddress: true,
|
||||||
|
showBilledToAddress: true,
|
||||||
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
|
total: '$1000.00',
|
||||||
|
totalLabel: 'Total',
|
||||||
|
showTotal: true,
|
||||||
|
|
||||||
|
subtotal: '1000/00',
|
||||||
|
subtotalLabel: 'Subtotal',
|
||||||
|
showSubtotal: true,
|
||||||
|
|
||||||
|
showCustomerNote: true,
|
||||||
|
customerNote:
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||||
|
customerNoteLabel: 'Customer Note',
|
||||||
|
|
||||||
|
showTermsConditions: true,
|
||||||
|
termsConditions:
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||||
|
termsConditionsLabel: 'Terms & Conditions',
|
||||||
|
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
item: 'Simply dummy text',
|
||||||
|
description: 'Simply dummy text of the printing and typesetting',
|
||||||
|
rate: '1',
|
||||||
|
quantity: '1000',
|
||||||
|
total: '$1000.00',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showReceiptNumber: true,
|
||||||
|
receiptNumberLabel: 'Receipt Number',
|
||||||
|
receiptNumebr: '346D3D40-0001',
|
||||||
|
|
||||||
|
receiptDate: 'September 3, 2024',
|
||||||
|
showReceiptDate: true,
|
||||||
|
receiptDateLabel: 'Receipt Date',
|
||||||
|
};
|
||||||
|
|||||||
6
packages/server/src/services/Sales/Receipts/utils.ts
Normal file
6
packages/server/src/services/Sales/Receipts/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const transformReceiptToBrandingTemplateAttributes = () => {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
@@ -12,10 +12,15 @@ import {
|
|||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { If, Icon, FormattedMessage as T, Group } from '@/components';
|
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
||||||
|
import {
|
||||||
|
BrandingThemeFormGroup,
|
||||||
|
BrandingThemeSelectButton,
|
||||||
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
import { useCreditNoteFormBrandingTemplatesOptions } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credit note floating actions.
|
* Credit note floating actions.
|
||||||
@@ -74,6 +79,8 @@ export default function CreditNoteFloatingActions() {
|
|||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const brandingTemplatesOptions = useCreditNoteFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<Group
|
||||||
spacing={10}
|
spacing={10}
|
||||||
@@ -190,6 +197,25 @@ export default function CreditNoteFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
|
<BrandingThemeFormGroup
|
||||||
|
name={'pdf_template_id'}
|
||||||
|
label={'Branding'}
|
||||||
|
inline
|
||||||
|
fastField
|
||||||
|
style={{ marginLeft: 20 }}
|
||||||
|
>
|
||||||
|
<FSelect
|
||||||
|
name={'pdf_template_id'}
|
||||||
|
items={brandingTemplatesOptions}
|
||||||
|
input={({ activeItem, text, label, value }) => (
|
||||||
|
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
|
||||||
|
)}
|
||||||
|
filterable={false}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</BrandingThemeFormGroup>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
useSettingsCreditNotes,
|
useSettingsCreditNotes,
|
||||||
useInvoice,
|
useInvoice,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
||||||
|
|
||||||
const CreditNoteFormContext = React.createContext();
|
const CreditNoteFormContext = React.createContext();
|
||||||
|
|
||||||
@@ -73,6 +74,10 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
|
|||||||
isSuccess: isBranchesSuccess,
|
isSuccess: isBranchesSuccess,
|
||||||
} = useBranches({}, { enabled: isBranchFeatureCan });
|
} = useBranches({}, { enabled: isBranchFeatureCan });
|
||||||
|
|
||||||
|
// Fetches branding templates of invoice.
|
||||||
|
const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } =
|
||||||
|
useGetPdfTemplates({ resource: 'PaymentReceive' });
|
||||||
|
|
||||||
// Handle fetching settings.
|
// Handle fetching settings.
|
||||||
useSettingsCreditNotes();
|
useSettingsCreditNotes();
|
||||||
|
|
||||||
@@ -115,13 +120,18 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
|
|||||||
createCreditNoteMutate,
|
createCreditNoteMutate,
|
||||||
editCreditNoteMutate,
|
editCreditNoteMutate,
|
||||||
setSubmitPayload,
|
setSubmitPayload,
|
||||||
|
|
||||||
|
// Branding templates.
|
||||||
|
brandingTemplates,
|
||||||
|
isBrandingTemplatesLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLoading =
|
const isLoading =
|
||||||
isItemsLoading ||
|
isItemsLoading ||
|
||||||
isCustomersLoading ||
|
isCustomersLoading ||
|
||||||
isCreditNoteLoading ||
|
isCreditNoteLoading ||
|
||||||
isInvoiceLoading;
|
isInvoiceLoading ||
|
||||||
|
isBrandingTemplatesLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider loading={isLoading} name={'credit-note-form'}>
|
<DashboardInsider loading={isLoading} name={'credit-note-form'}>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
transformAttachmentsToForm,
|
transformAttachmentsToForm,
|
||||||
transformAttachmentsToRequest,
|
transformAttachmentsToRequest,
|
||||||
} from '@/containers/Attachments/utils';
|
} from '@/containers/Attachments/utils';
|
||||||
|
import { convertBrandingTemplatesToOptions } from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 1;
|
export const MIN_LINES_NUMBER = 1;
|
||||||
|
|
||||||
@@ -54,7 +55,8 @@ export const defaultCreditNote = {
|
|||||||
exchange_rate: 1,
|
exchange_rate: 1,
|
||||||
currency_code: '',
|
currency_code: '',
|
||||||
entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)],
|
entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)],
|
||||||
attachments: []
|
attachments: [],
|
||||||
|
pdf_template_id: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,3 +216,13 @@ export const useCreditNoteIsForeignCustomer = () => {
|
|||||||
);
|
);
|
||||||
return isForeignCustomer;
|
return isForeignCustomer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const useCreditNoteFormBrandingTemplatesOptions = () => {
|
||||||
|
const { brandingTemplates } = useCreditNoteFormContext();
|
||||||
|
|
||||||
|
return React.useMemo(
|
||||||
|
() => convertBrandingTemplatesToOptions(brandingTemplates),
|
||||||
|
[brandingTemplates],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -11,11 +11,16 @@ import {
|
|||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { If, Icon, FormattedMessage as T, Group } from '@/components';
|
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { useEstimateFormContext } from './EstimateFormProvider';
|
import { useEstimateFormContext } from './EstimateFormProvider';
|
||||||
|
import { useEstimateFormBrandingTemplatesOptions } from './utils';
|
||||||
|
import {
|
||||||
|
BrandingThemeFormGroup,
|
||||||
|
BrandingThemeSelectButton,
|
||||||
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimate floating actions bar.
|
* Estimate floating actions bar.
|
||||||
@@ -73,6 +78,8 @@ export default function EstimateFloatingActions() {
|
|||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const brandingTemplatesOptions = useEstimateFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<Group
|
||||||
spacing={10}
|
spacing={10}
|
||||||
@@ -193,6 +200,25 @@ export default function EstimateFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
|
<BrandingThemeFormGroup
|
||||||
|
name={'pdf_template_id'}
|
||||||
|
label={'Branding'}
|
||||||
|
inline
|
||||||
|
fastField
|
||||||
|
style={{ marginLeft: 20 }}
|
||||||
|
>
|
||||||
|
<FSelect
|
||||||
|
name={'pdf_template_id'}
|
||||||
|
items={brandingTemplatesOptions}
|
||||||
|
input={({ activeItem, text, label, value }) => (
|
||||||
|
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
|
||||||
|
)}
|
||||||
|
filterable={false}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</BrandingThemeFormGroup>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import {
|
|||||||
useCreateEstimate,
|
useCreateEstimate,
|
||||||
useEditEstimate,
|
useEditEstimate,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
import { Features } from '@/constants';
|
|
||||||
import { useProjects } from '@/containers/Projects/hooks';
|
import { useProjects } from '@/containers/Projects/hooks';
|
||||||
|
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
||||||
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import { ITEMS_FILTER_ROLES } from './utils';
|
import { ITEMS_FILTER_ROLES } from './utils';
|
||||||
|
|
||||||
@@ -71,6 +72,10 @@ function EstimateFormProvider({ query, estimateId, ...props }) {
|
|||||||
isLoading: isProjectsLoading,
|
isLoading: isProjectsLoading,
|
||||||
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
|
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
|
||||||
|
|
||||||
|
// Fetches branding templates of invoice.
|
||||||
|
const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } =
|
||||||
|
useGetPdfTemplates({ resource: 'SaleEstimate' });
|
||||||
|
|
||||||
// Handle fetch settings.
|
// Handle fetch settings.
|
||||||
useSettingsEstimates();
|
useSettingsEstimates();
|
||||||
|
|
||||||
@@ -112,13 +117,19 @@ function EstimateFormProvider({ query, estimateId, ...props }) {
|
|||||||
|
|
||||||
createEstimateMutate,
|
createEstimateMutate,
|
||||||
editEstimateMutate,
|
editEstimateMutate,
|
||||||
|
|
||||||
|
brandingTemplates,
|
||||||
|
isBrandingTemplatesLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isLoading =
|
||||||
|
isCustomersLoading ||
|
||||||
|
isItemsLoading ||
|
||||||
|
isEstimateLoading ||
|
||||||
|
isBrandingTemplatesLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider loading={isLoading} name={'estimate-form'}>
|
||||||
loading={isCustomersLoading || isItemsLoading || isEstimateLoading}
|
|
||||||
name={'estimate-form'}
|
|
||||||
>
|
|
||||||
<EstimateFormContext.Provider value={provider} {...props} />
|
<EstimateFormContext.Provider value={provider} {...props} />
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
transformAttachmentsToForm,
|
transformAttachmentsToForm,
|
||||||
transformAttachmentsToRequest,
|
transformAttachmentsToRequest,
|
||||||
} from '@/containers/Attachments/utils';
|
} from '@/containers/Attachments/utils';
|
||||||
|
import { convertBrandingTemplatesToOptions } from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 1;
|
export const MIN_LINES_NUMBER = 1;
|
||||||
|
|
||||||
@@ -60,7 +61,8 @@ export const defaultEstimate = {
|
|||||||
exchange_rate: 1,
|
exchange_rate: 1,
|
||||||
currency_code: '',
|
currency_code: '',
|
||||||
entries: [...repeatValue(defaultEstimateEntry, MIN_LINES_NUMBER)],
|
entries: [...repeatValue(defaultEstimateEntry, MIN_LINES_NUMBER)],
|
||||||
attachments: []
|
attachments: [],
|
||||||
|
pdf_template_id: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
@@ -262,3 +264,12 @@ export const resetFormState = ({ initialValues, values, resetForm }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useEstimateFormBrandingTemplatesOptions = () => {
|
||||||
|
const { brandingTemplates } = useEstimateFormContext();
|
||||||
|
|
||||||
|
return React.useMemo(
|
||||||
|
() => convertBrandingTemplatesToOptions(brandingTemplates),
|
||||||
|
[brandingTemplates],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
DashboardFilterButton,
|
DashboardFilterButton,
|
||||||
DashboardRowsHeightButton,
|
DashboardRowsHeightButton,
|
||||||
DashboardActionsBar,
|
DashboardActionsBar,
|
||||||
|
FSelect,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
|
|
||||||
import withEstimates from './withEstimates';
|
import withEstimates from './withEstimates';
|
||||||
@@ -42,6 +43,10 @@ import { compose } from '@/utils';
|
|||||||
import { DialogsName } from '@/constants/dialogs';
|
import { DialogsName } from '@/constants/dialogs';
|
||||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||||
import { DRAWERS } from '@/constants/drawers';
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
import {
|
||||||
|
BrandingThemeFormGroup,
|
||||||
|
BrandingThemeSelectButton,
|
||||||
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimates list actions bar.
|
* Estimates list actions bar.
|
||||||
|
|||||||
@@ -12,10 +12,15 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Group, Icon, FormattedMessage as T } from '@/components';
|
import { FSelect, Group, Icon, FormattedMessage as T } from '@/components';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
|
import {
|
||||||
|
BrandingThemeFormGroup,
|
||||||
|
BrandingThemeSelectButton,
|
||||||
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
import { usePaymentReceivedFormBrandingTemplatesOptions } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment receive floating actions bar.
|
* Payment receive floating actions bar.
|
||||||
@@ -53,6 +58,9 @@ export default function PaymentReceiveFormFloatingActions() {
|
|||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const brandingTemplatesOpts =
|
||||||
|
usePaymentReceivedFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<Group
|
||||||
spacing={10}
|
spacing={10}
|
||||||
@@ -109,6 +117,25 @@ export default function PaymentReceiveFormFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
|
<BrandingThemeFormGroup
|
||||||
|
name={'pdf_template_id'}
|
||||||
|
label={'Branding'}
|
||||||
|
inline
|
||||||
|
fastField
|
||||||
|
style={{ marginLeft: 20 }}
|
||||||
|
>
|
||||||
|
<FSelect
|
||||||
|
name={'pdf_template_id'}
|
||||||
|
items={brandingTemplatesOpts}
|
||||||
|
input={({ activeItem, text, label, value }) => (
|
||||||
|
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
|
||||||
|
)}
|
||||||
|
filterable={false}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</BrandingThemeFormGroup>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
useCreatePaymentReceive,
|
useCreatePaymentReceive,
|
||||||
useEditPaymentReceive,
|
useEditPaymentReceive,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
||||||
|
|
||||||
// Payment receive form context.
|
// Payment receive form context.
|
||||||
const PaymentReceiveFormContext = createContext();
|
const PaymentReceiveFormContext = createContext();
|
||||||
@@ -65,6 +66,10 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
|
|||||||
isLoading: isProjectsLoading,
|
isLoading: isProjectsLoading,
|
||||||
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
|
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
|
||||||
|
|
||||||
|
// Fetches branding templates of payment received module.
|
||||||
|
const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } =
|
||||||
|
useGetPdfTemplates({ resource: 'PaymentReceive' });
|
||||||
|
|
||||||
// Detarmines whether the new mode.
|
// Detarmines whether the new mode.
|
||||||
const isNewMode = !paymentReceiveId;
|
const isNewMode = !paymentReceiveId;
|
||||||
|
|
||||||
@@ -102,13 +107,20 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
|
|||||||
|
|
||||||
isExcessConfirmed,
|
isExcessConfirmed,
|
||||||
setIsExcessConfirmed,
|
setIsExcessConfirmed,
|
||||||
|
|
||||||
|
// Branding templates
|
||||||
|
brandingTemplates,
|
||||||
|
isBrandingTemplatesLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isLoading =
|
||||||
|
isPaymentLoading ||
|
||||||
|
isAccountsLoading ||
|
||||||
|
isCustomersLoading ||
|
||||||
|
isBrandingTemplatesLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider loading={isLoading} name={'payment-receive-form'}>
|
||||||
loading={isPaymentLoading || isAccountsLoading || isCustomersLoading}
|
|
||||||
name={'payment-receive-form'}
|
|
||||||
>
|
|
||||||
<PaymentReceiveFormContext.Provider value={provider} {...props} />
|
<PaymentReceiveFormContext.Provider value={provider} {...props} />
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
transformAttachmentsToForm,
|
transformAttachmentsToForm,
|
||||||
transformAttachmentsToRequest,
|
transformAttachmentsToRequest,
|
||||||
} from '@/containers/Attachments/utils';
|
} from '@/containers/Attachments/utils';
|
||||||
|
import { convertBrandingTemplatesToOptions } from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
|
||||||
// Default payment receive entry.
|
// Default payment receive entry.
|
||||||
export const defaultPaymentReceiveEntry = {
|
export const defaultPaymentReceiveEntry = {
|
||||||
@@ -44,10 +45,11 @@ export const defaultPaymentReceive = {
|
|||||||
statement: '',
|
statement: '',
|
||||||
amount: '',
|
amount: '',
|
||||||
currency_code: '',
|
currency_code: '',
|
||||||
branch_id: '',
|
|
||||||
exchange_rate: 1,
|
exchange_rate: 1,
|
||||||
entries: [],
|
entries: [],
|
||||||
attachments: [],
|
attachments: [],
|
||||||
|
branch_id: '',
|
||||||
|
pdf_template_id: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultRequestPaymentEntry = {
|
export const defaultRequestPaymentEntry = {
|
||||||
@@ -303,3 +305,12 @@ export const getExceededAmountFromValues = (values) => {
|
|||||||
|
|
||||||
return totalAmount - totalApplied;
|
return totalAmount - totalApplied;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const usePaymentReceivedFormBrandingTemplatesOptions = () => {
|
||||||
|
const { brandingTemplates } = usePaymentReceiveFormContext();
|
||||||
|
|
||||||
|
return React.useMemo(
|
||||||
|
() => convertBrandingTemplatesToOptions(brandingTemplates),
|
||||||
|
[brandingTemplates],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -11,12 +11,17 @@ import {
|
|||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { Group, FormattedMessage as T } from '@/components';
|
import { FSelect, Group, FormattedMessage as T } from '@/components';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import { If, Icon } from '@/components';
|
import { If, Icon } from '@/components';
|
||||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||||
|
import { useReceiptFormBrandingTemplatesOptions } from './utils';
|
||||||
|
import {
|
||||||
|
BrandingThemeFormGroup,
|
||||||
|
BrandingThemeSelectButton,
|
||||||
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receipt floating actions bar.
|
* Receipt floating actions bar.
|
||||||
@@ -76,6 +81,8 @@ export default function ReceiptFormFloatingActions() {
|
|||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const brandingTemplatesOptions = useReceiptFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<Group
|
||||||
spacing={10}
|
spacing={10}
|
||||||
@@ -191,6 +198,25 @@ export default function ReceiptFormFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
|
<BrandingThemeFormGroup
|
||||||
|
name={'pdf_template_id'}
|
||||||
|
label={'Branding'}
|
||||||
|
inline
|
||||||
|
fastField
|
||||||
|
style={{ marginLeft: 20 }}
|
||||||
|
>
|
||||||
|
<FSelect
|
||||||
|
name={'pdf_template_id'}
|
||||||
|
items={brandingTemplatesOptions}
|
||||||
|
input={({ activeItem, text, label, value }) => (
|
||||||
|
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
|
||||||
|
)}
|
||||||
|
filterable={false}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</BrandingThemeFormGroup>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
useEditReceipt,
|
useEditReceipt,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
import { useProjects } from '@/containers/Projects/hooks';
|
import { useProjects } from '@/containers/Projects/hooks';
|
||||||
|
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
||||||
|
|
||||||
const ReceiptFormContext = createContext();
|
const ReceiptFormContext = createContext();
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ function ReceiptFormProvider({ receiptId, ...props }) {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle fetch Items data table or list
|
// Handle fetch Items data table or list.
|
||||||
const {
|
const {
|
||||||
data: { items },
|
data: { items },
|
||||||
isLoading: isItemsLoading,
|
isLoading: isItemsLoading,
|
||||||
@@ -85,13 +86,16 @@ function ReceiptFormProvider({ receiptId, ...props }) {
|
|||||||
page_size: 10000,
|
page_size: 10000,
|
||||||
stringified_filter_roles: stringifiedFilterRoles,
|
stringified_filter_roles: stringifiedFilterRoles,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch project list.
|
// Fetch project list.
|
||||||
const {
|
const {
|
||||||
data: { projects },
|
data: { projects },
|
||||||
isLoading: isProjectsLoading,
|
isLoading: isProjectsLoading,
|
||||||
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
|
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
|
||||||
|
|
||||||
|
// Fetches branding templates of receipt.
|
||||||
|
const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } =
|
||||||
|
useGetPdfTemplates({ resource: 'SaleReceipt' });
|
||||||
|
|
||||||
// Fetch receipt settings.
|
// Fetch receipt settings.
|
||||||
const { isLoading: isSettingLoading } = useSettingsReceipts();
|
const { isLoading: isSettingLoading } = useSettingsReceipts();
|
||||||
|
|
||||||
@@ -101,7 +105,6 @@ function ReceiptFormProvider({ receiptId, ...props }) {
|
|||||||
const [submitPayload, setSubmitPayload] = useState({});
|
const [submitPayload, setSubmitPayload] = useState({});
|
||||||
|
|
||||||
const isNewMode = !receiptId;
|
const isNewMode = !receiptId;
|
||||||
|
|
||||||
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
|
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
|
||||||
|
|
||||||
const provider = {
|
const provider = {
|
||||||
@@ -130,6 +133,10 @@ function ReceiptFormProvider({ receiptId, ...props }) {
|
|||||||
createReceiptMutate,
|
createReceiptMutate,
|
||||||
editReceiptMutate,
|
editReceiptMutate,
|
||||||
setSubmitPayload,
|
setSubmitPayload,
|
||||||
|
|
||||||
|
// Branding templates
|
||||||
|
brandingTemplates,
|
||||||
|
isBrandingTemplatesLoading
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
transformAttachmentsToForm,
|
transformAttachmentsToForm,
|
||||||
transformAttachmentsToRequest,
|
transformAttachmentsToRequest,
|
||||||
} from '@/containers/Attachments/utils';
|
} from '@/containers/Attachments/utils';
|
||||||
|
import { convertBrandingTemplatesToOptions } from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 1;
|
export const MIN_LINES_NUMBER = 1;
|
||||||
|
|
||||||
@@ -61,6 +62,7 @@ export const defaultReceipt = {
|
|||||||
currency_code: '',
|
currency_code: '',
|
||||||
entries: [...repeatValue(defaultReceiptEntry, MIN_LINES_NUMBER)],
|
entries: [...repeatValue(defaultReceiptEntry, MIN_LINES_NUMBER)],
|
||||||
attachments: [],
|
attachments: [],
|
||||||
|
pdf_template_id: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
@@ -272,3 +274,12 @@ export const resetFormState = ({ initialValues, values, resetForm }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useReceiptFormBrandingTemplatesOptions = () => {
|
||||||
|
const { brandingTemplates } = useReceiptFormContext();
|
||||||
|
|
||||||
|
return React.useMemo(
|
||||||
|
() => convertBrandingTemplatesToOptions(brandingTemplates),
|
||||||
|
[brandingTemplates],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user