mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 15:50:32 +00:00
feat: invoice, estimate and receipt printing.
This commit is contained in:
@@ -53,4 +53,22 @@ services:
|
|||||||
- nginx
|
- nginx
|
||||||
volumes:
|
volumes:
|
||||||
- ./certbot/letsencrypt/:/var/www/letsencrypt
|
- ./certbot/letsencrypt/:/var/www/letsencrypt
|
||||||
- ./certbot/certs/:/var/certs
|
- ./certbot/certs/:/var/certs
|
||||||
|
|
||||||
|
browserless:
|
||||||
|
image: browserless/chrome:latest
|
||||||
|
environment:
|
||||||
|
- DEBUG=browserless:*
|
||||||
|
- MAX_CONCURRENT_SESSIONS=1
|
||||||
|
- MAX_QUEUE_LENGTH=20
|
||||||
|
- PREBOOT_CHROME=true
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- ENABLE_DEBUGGER=false
|
||||||
|
- PORT=3000
|
||||||
|
- WORKSPACE_DELETE_EXPIRED=true
|
||||||
|
container_name: "browserless_bigcapital"
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
expose:
|
||||||
|
- "3000"
|
||||||
@@ -36,4 +36,5 @@ LICENSES_AUTH_USER=root
|
|||||||
LICENSES_AUTH_PASSWORD=root
|
LICENSES_AUTH_PASSWORD=root
|
||||||
|
|
||||||
AGENDASH_AUTH_USER=agendash
|
AGENDASH_AUTH_USER=agendash
|
||||||
AGENDASH_AUTH_PASSWORD=123123
|
AGENDASH_AUTH_PASSWORD=123123
|
||||||
|
BROWSER_WS_ENDPOINT=ws://localhost:3000/
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
"copy-18n": "cpy --cwd=src/locales --parents '**/*.json' ../../build/locales",
|
"copy-18n": "cpy --cwd=src/locales --parents '**/*.json' ../../build/locales",
|
||||||
"clear": "rimraf build",
|
"clear": "rimraf build",
|
||||||
"build:ts": "tsc -p tsconfig.json",
|
"build:ts": "tsc -p tsconfig.json",
|
||||||
|
"build:resources": "gulp --gulpfile=scripts/gulpfile.js styles",
|
||||||
"build": "npm-run-all clear build:ts copy-18n"
|
"build": "npm-run-all clear build:ts copy-18n"
|
||||||
},
|
},
|
||||||
"author": "Ahmed Bouhuolia, <a.bouhuolia@gmail.com>",
|
"author": "Ahmed Bouhuolia, <a.bouhuolia@gmail.com>",
|
||||||
@@ -47,6 +48,8 @@
|
|||||||
"express-fileupload": "^1.1.7-alpha.3",
|
"express-fileupload": "^1.1.7-alpha.3",
|
||||||
"express-oauth-server": "^2.0.0",
|
"express-oauth-server": "^2.0.0",
|
||||||
"express-validator": "^6.8.0",
|
"express-validator": "^6.8.0",
|
||||||
|
"gulp": "^4.0.2",
|
||||||
|
"gulp-sass": "^5.0.0",
|
||||||
"helmet": "^3.21.0",
|
"helmet": "^3.21.0",
|
||||||
"i18n": "^0.8.5",
|
"i18n": "^0.8.5",
|
||||||
"is-my-json-valid": "^2.20.5",
|
"is-my-json-valid": "^2.20.5",
|
||||||
@@ -73,6 +76,8 @@
|
|||||||
"objection-filter": "^4.0.1",
|
"objection-filter": "^4.0.1",
|
||||||
"objection-soft-delete": "^1.0.7",
|
"objection-soft-delete": "^1.0.7",
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
|
"pug": "^3.0.2",
|
||||||
|
"puppeteer": "^10.2.0",
|
||||||
"ramda": "^0.27.1",
|
"ramda": "^0.27.1",
|
||||||
"rate-limiter-flexible": "^2.1.14",
|
"rate-limiter-flexible": "^2.1.14",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
@@ -103,32 +108,13 @@
|
|||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"nyc": "^14.1.1",
|
"nyc": "^14.1.1",
|
||||||
"regenerator-runtime": "^0.13.7",
|
"regenerator-runtime": "^0.13.7",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"sass": "^1.37.5",
|
||||||
"sinon": "^7.4.2",
|
"sinon": "^7.4.2",
|
||||||
"ts-node": "^9.0.0",
|
"ts-node": "^9.0.0",
|
||||||
"typedi": "^0.8.0",
|
"typedi": "^0.8.0",
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^3.9.7",
|
||||||
"webpack-cli": "^4.6.0",
|
"webpack-cli": "^4.6.0"
|
||||||
"rimraf": "^3.0.2"
|
|
||||||
},
|
|
||||||
"_moduleAliases": {
|
|
||||||
"loaders": "build/loaders",
|
|
||||||
"collection": "build/collection",
|
|
||||||
"config": "build/config",
|
|
||||||
"api": "build/api",
|
|
||||||
"data": "build/data",
|
|
||||||
"database": "build/database",
|
|
||||||
"decorators": "build/decorators",
|
|
||||||
"exceptions": "build/exceptions",
|
|
||||||
"interfaces": "build/interfaces",
|
|
||||||
"jobs": "build/jobs",
|
|
||||||
"lib": "build/lib",
|
|
||||||
"utils": "build/utils",
|
|
||||||
"locales": "build/locales",
|
|
||||||
"models": "build/models",
|
|
||||||
"repositories": "build/repositories",
|
|
||||||
"services": "build/services",
|
|
||||||
"subscribers": "build/subscribers",
|
|
||||||
"system": "build/system"
|
|
||||||
},
|
},
|
||||||
"_moduleAliases": {}
|
"_moduleAliases": {}
|
||||||
}
|
}
|
||||||
|
|||||||
521
server/resources/css/modules/estimate.css
Normal file
521
server/resources/css/modules/estimate.css
Normal file
@@ -0,0 +1,521 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
html {
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
/* 1 */
|
||||||
|
height: 0;
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none;
|
||||||
|
/* 1 */
|
||||||
|
text-decoration: underline;
|
||||||
|
/* 2 */
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 100%;
|
||||||
|
/* 1 */
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
margin: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
/* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type=button]::-moz-focus-inner,
|
||||||
|
[type=reset]::-moz-focus-inner,
|
||||||
|
[type=submit]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type=button]:-moz-focusring,
|
||||||
|
[type=reset]:-moz-focusring,
|
||||||
|
[type=submit]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
color: inherit;
|
||||||
|
/* 2 */
|
||||||
|
display: table;
|
||||||
|
/* 1 */
|
||||||
|
max-width: 100%;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 3 */
|
||||||
|
white-space: normal;
|
||||||
|
/* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
[type=checkbox],
|
||||||
|
[type=radio] {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
[type=number]::-webkit-inner-spin-button,
|
||||||
|
[type=number]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
[type=search] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
/* 1 */
|
||||||
|
outline-offset: -2px;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
[type=search]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
/* 1 */
|
||||||
|
font: inherit;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #f8f9fa;
|
||||||
|
font-family: "Noto Sans", sans-serif;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
background: white;
|
||||||
|
display: block;
|
||||||
|
margin: 0.5cm auto;
|
||||||
|
box-shadow: rgba(122, 136, 146, 0.15) 0px 1px 3px 1px;
|
||||||
|
width: 21cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
.page {
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: 0 0 0;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page[size=A4] {
|
||||||
|
width: 21cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
.page[size=A4][layout=landscape] {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 21cm;
|
||||||
|
}
|
||||||
|
.page[size=A3] {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 42cm;
|
||||||
|
}
|
||||||
|
.page[size=A3][layout=landscape] {
|
||||||
|
width: 42cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
.page[size=A5] {
|
||||||
|
width: 14.8cm;
|
||||||
|
height: 21cm;
|
||||||
|
}
|
||||||
|
.page[size=A5][layout=landscape] {
|
||||||
|
width: 21cm;
|
||||||
|
height: 14.8cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.estimate {
|
||||||
|
text-align: left;
|
||||||
|
padding: 45px;
|
||||||
|
}
|
||||||
|
.estimate__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 0 60px;
|
||||||
|
}
|
||||||
|
.estimate__header .organization .title {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
.estimate__header .paper .title {
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
.estimate__meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
.estimate__meta-item {
|
||||||
|
flex: 0 1 25%;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.estimate__meta-item .value {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.estimate__meta-item .label {
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.estimate__meta-item--amount {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
.estimate__meta-item--amount .value {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.estimate__meta-item--billed-to {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
.estimate__table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
.estimate__table table {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #000;
|
||||||
|
border-top: 2px solid #000;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.estimate__table table thead th,
|
||||||
|
.estimate__table table tbody tr td {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.estimate__table table thead tr {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.estimate__table table thead th {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.estimate__table table thead th:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.estimate__table table thead th:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.estimate__table table tbody tr td {
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #cecbcb;
|
||||||
|
}
|
||||||
|
.estimate__table table tbody tr td:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.estimate__table table tbody tr td::last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.estimate__table table thead tr th.item {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
.estimate__table table thead tr th.rate {
|
||||||
|
width: 18%;
|
||||||
|
}
|
||||||
|
.estimate__table table thead tr th.quantity {
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
.estimate__table table thead tr th.total {
|
||||||
|
width: 21%;
|
||||||
|
}
|
||||||
|
.estimate__conditions__title {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
527
server/resources/css/modules/invoice.css
Normal file
527
server/resources/css/modules/invoice.css
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
html {
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
/* 1 */
|
||||||
|
height: 0;
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none;
|
||||||
|
/* 1 */
|
||||||
|
text-decoration: underline;
|
||||||
|
/* 2 */
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 100%;
|
||||||
|
/* 1 */
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
margin: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
/* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type=button]::-moz-focus-inner,
|
||||||
|
[type=reset]::-moz-focus-inner,
|
||||||
|
[type=submit]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type=button]:-moz-focusring,
|
||||||
|
[type=reset]:-moz-focusring,
|
||||||
|
[type=submit]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
color: inherit;
|
||||||
|
/* 2 */
|
||||||
|
display: table;
|
||||||
|
/* 1 */
|
||||||
|
max-width: 100%;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 3 */
|
||||||
|
white-space: normal;
|
||||||
|
/* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
[type=checkbox],
|
||||||
|
[type=radio] {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
[type=number]::-webkit-inner-spin-button,
|
||||||
|
[type=number]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
[type=search] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
/* 1 */
|
||||||
|
outline-offset: -2px;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
[type=search]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
/* 1 */
|
||||||
|
font: inherit;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #f8f9fa;
|
||||||
|
font-family: "Noto Sans", sans-serif;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
background: white;
|
||||||
|
display: block;
|
||||||
|
margin: 0.5cm auto;
|
||||||
|
box-shadow: rgba(122, 136, 146, 0.15) 0px 1px 3px 1px;
|
||||||
|
width: 21cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
.page {
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: 0 0 0;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page[size=A4] {
|
||||||
|
width: 21cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
.page[size=A4][layout=landscape] {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 21cm;
|
||||||
|
}
|
||||||
|
.page[size=A3] {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 42cm;
|
||||||
|
}
|
||||||
|
.page[size=A3][layout=landscape] {
|
||||||
|
width: 42cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
.page[size=A5] {
|
||||||
|
width: 14.8cm;
|
||||||
|
height: 21cm;
|
||||||
|
}
|
||||||
|
.page[size=A5][layout=landscape] {
|
||||||
|
width: 21cm;
|
||||||
|
height: 14.8cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice {
|
||||||
|
text-align: left;
|
||||||
|
padding: 45px;
|
||||||
|
}
|
||||||
|
.invoice__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 0 60px;
|
||||||
|
}
|
||||||
|
.invoice__header .organization .title {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
.invoice__header .paper .title {
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
.invoice__meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
.invoice__meta-item {
|
||||||
|
flex: 0 1 25%;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.invoice__meta-item .value {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.invoice__meta-item .label {
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.invoice__meta-item--amount {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
.invoice__meta-item--amount .value {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.invoice__meta-item--billed-to {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
.invoice__table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
.invoice__table table {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #000;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.invoice__table table thead th,
|
||||||
|
.invoice__table table tbody tr td {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.invoice__table table thead tr {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.invoice__table table thead tr th {
|
||||||
|
border-top: 2px solid #000;
|
||||||
|
}
|
||||||
|
.invoice__table table thead th {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.invoice__table table thead th:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.invoice__table table thead th:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.invoice__table table tbody tr td {
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #cecbcb;
|
||||||
|
}
|
||||||
|
.invoice__table table tbody tr td:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.invoice__table table tbody tr td::last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.invoice__table table thead tr th.item {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
.invoice__table table thead tr th.rate {
|
||||||
|
width: 18%;
|
||||||
|
}
|
||||||
|
.invoice__table table thead tr th.quantity {
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
.invoice__table table thead tr th.total {
|
||||||
|
width: 21%;
|
||||||
|
}
|
||||||
|
.invoice__table table .description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.invoice__conditions__title {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
521
server/resources/css/modules/receipt.css
Normal file
521
server/resources/css/modules/receipt.css
Normal file
@@ -0,0 +1,521 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
html {
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
/* 1 */
|
||||||
|
height: 0;
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none;
|
||||||
|
/* 1 */
|
||||||
|
text-decoration: underline;
|
||||||
|
/* 2 */
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 100%;
|
||||||
|
/* 1 */
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
margin: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
/* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type=button]::-moz-focus-inner,
|
||||||
|
[type=reset]::-moz-focus-inner,
|
||||||
|
[type=submit]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type=button]:-moz-focusring,
|
||||||
|
[type=reset]:-moz-focusring,
|
||||||
|
[type=submit]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
color: inherit;
|
||||||
|
/* 2 */
|
||||||
|
display: table;
|
||||||
|
/* 1 */
|
||||||
|
max-width: 100%;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 3 */
|
||||||
|
white-space: normal;
|
||||||
|
/* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
[type=checkbox],
|
||||||
|
[type=radio] {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
[type=number]::-webkit-inner-spin-button,
|
||||||
|
[type=number]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
[type=search] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
/* 1 */
|
||||||
|
outline-offset: -2px;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
[type=search]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
/* 1 */
|
||||||
|
font: inherit;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #f8f9fa;
|
||||||
|
font-family: "Noto Sans", sans-serif;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
background: white;
|
||||||
|
display: block;
|
||||||
|
margin: 0.5cm auto;
|
||||||
|
box-shadow: rgba(122, 136, 146, 0.15) 0px 1px 3px 1px;
|
||||||
|
width: 21cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
.page {
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: 0 0 0;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page[size=A4] {
|
||||||
|
width: 21cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
.page[size=A4][layout=landscape] {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 21cm;
|
||||||
|
}
|
||||||
|
.page[size=A3] {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 42cm;
|
||||||
|
}
|
||||||
|
.page[size=A3][layout=landscape] {
|
||||||
|
width: 42cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
.page[size=A5] {
|
||||||
|
width: 14.8cm;
|
||||||
|
height: 21cm;
|
||||||
|
}
|
||||||
|
.page[size=A5][layout=landscape] {
|
||||||
|
width: 21cm;
|
||||||
|
height: 14.8cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.receipt {
|
||||||
|
text-align: left;
|
||||||
|
padding: 45px;
|
||||||
|
}
|
||||||
|
.receipt__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 0 60px;
|
||||||
|
}
|
||||||
|
.receipt__header .organization .title {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
.receipt__header .paper .title {
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
.receipt__meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
.receipt__meta-item {
|
||||||
|
flex: 0 1 25%;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.receipt__meta-item .value {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.receipt__meta-item .label {
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.receipt__meta-item--amount {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
.receipt__meta-item--amount .value {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.receipt__meta-item--billed-to {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
.receipt__table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
.receipt__table table {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #000;
|
||||||
|
border-top: 2px solid #000;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.receipt__table table thead th,
|
||||||
|
.receipt__table table tbody tr td {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.receipt__table table thead tr {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.receipt__table table thead th {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.receipt__table table thead th:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.receipt__table table thead th:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.receipt__table table tbody tr td {
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #cecbcb;
|
||||||
|
}
|
||||||
|
.receipt__table table tbody tr td:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.receipt__table table tbody tr td::last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.receipt__table table thead tr th.item {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
.receipt__table table thead tr th.rate {
|
||||||
|
width: 18%;
|
||||||
|
}
|
||||||
|
.receipt__table table thead tr th.quantity {
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
.receipt__table table thead tr th.total {
|
||||||
|
width: 21%;
|
||||||
|
}
|
||||||
|
.receipt__conditions__title {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
23
server/resources/scss/base.scss
Normal file
23
server/resources/scss/base.scss
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
@import "./normalize.scss";
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit; // 2
|
||||||
|
text-align: -webkit-match-parent; // 3
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
57
server/resources/scss/layouts/paper-layout.scss
Normal file
57
server/resources/scss/layouts/paper-layout.scss
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
@import "../base.scss";
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #f8f9fa;
|
||||||
|
font-family: 'Noto Sans', sans-serif;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
background: white;
|
||||||
|
display: block;
|
||||||
|
margin: 0.5cm auto;
|
||||||
|
box-shadow: rgba(122, 136, 146, 0.15) 0px 1px 3px 1px;
|
||||||
|
width: 21cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: 0 0 0;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[size="A4"] {
|
||||||
|
width: 21cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[size="A4"][layout="landscape"] {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 21cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[size="A3"] {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 42cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[size="A3"][layout="landscape"] {
|
||||||
|
width: 42cm;
|
||||||
|
height: 29.7cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[size="A5"] {
|
||||||
|
width: 14.8cm;
|
||||||
|
height: 21cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[size="A5"][layout="landscape"] {
|
||||||
|
width: 21cm;
|
||||||
|
height: 14.8cm;
|
||||||
|
}
|
||||||
|
}
|
||||||
141
server/resources/scss/modules/estimate.scss
Normal file
141
server/resources/scss/modules/estimate.scss
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
@import "../layouts/paper-layout.scss";
|
||||||
|
|
||||||
|
.estimate{
|
||||||
|
text-align: left;
|
||||||
|
padding: 45px;
|
||||||
|
|
||||||
|
&__header{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 0 60px;
|
||||||
|
|
||||||
|
.organization{
|
||||||
|
|
||||||
|
.title{
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper{
|
||||||
|
|
||||||
|
.title{
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
|
||||||
|
&-item{
|
||||||
|
flex: 0 1 25%;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.value{
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.label{
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--amount{
|
||||||
|
flex: 0 1 50%;
|
||||||
|
|
||||||
|
.value{
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--billed-to{
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #000;
|
||||||
|
border-top: 2px solid #000;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
thead th,
|
||||||
|
tbody tr td {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
thead{
|
||||||
|
tr {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thead th {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&:first-child{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody tr td {
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #cecbcb;
|
||||||
|
|
||||||
|
&:first-child{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::last-child{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thead tr th{
|
||||||
|
&.item{
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
&.rate{
|
||||||
|
width: 18%;
|
||||||
|
}
|
||||||
|
&.quantity{
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
&.total{
|
||||||
|
width: 21%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__conditions{
|
||||||
|
|
||||||
|
&__title{
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
147
server/resources/scss/modules/invoice.scss
Normal file
147
server/resources/scss/modules/invoice.scss
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
@import "../layouts/paper-layout.scss";
|
||||||
|
|
||||||
|
.invoice{
|
||||||
|
text-align: left;
|
||||||
|
padding: 45px;
|
||||||
|
|
||||||
|
&__header{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 0 60px;
|
||||||
|
|
||||||
|
.organization{
|
||||||
|
|
||||||
|
.title{
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper{
|
||||||
|
|
||||||
|
.title{
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
|
||||||
|
&-item{
|
||||||
|
flex: 0 1 25%;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.value{
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.label{
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--amount{
|
||||||
|
flex: 0 1 50%;
|
||||||
|
|
||||||
|
.value{
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&--billed-to{
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #000;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
thead th,
|
||||||
|
tbody tr td {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
thead{
|
||||||
|
tr {
|
||||||
|
color: #000;
|
||||||
|
|
||||||
|
th{
|
||||||
|
border-top: 2px solid #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thead th {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&:first-child{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody tr td {
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #cecbcb;
|
||||||
|
|
||||||
|
&:first-child{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::last-child{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thead tr th{
|
||||||
|
&.item{
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
&.rate{
|
||||||
|
width: 18%;
|
||||||
|
}
|
||||||
|
&.quantity{
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
&.total{
|
||||||
|
width: 21%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.description{
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__conditions{
|
||||||
|
|
||||||
|
&__title{
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
140
server/resources/scss/modules/receipt.scss
Normal file
140
server/resources/scss/modules/receipt.scss
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
@import "../layouts/paper-layout.scss";
|
||||||
|
|
||||||
|
.receipt{
|
||||||
|
text-align: left;
|
||||||
|
padding: 45px;
|
||||||
|
|
||||||
|
&__header{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 0 60px;
|
||||||
|
|
||||||
|
.organization{
|
||||||
|
|
||||||
|
.title{
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper{
|
||||||
|
|
||||||
|
.title{
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
|
||||||
|
&-item{
|
||||||
|
flex: 0 1 25%;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.value{
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.label{
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--amount{
|
||||||
|
flex: 0 1 50%;
|
||||||
|
|
||||||
|
.value{
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&--billed-to{
|
||||||
|
flex: 0 1 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #000;
|
||||||
|
border-top: 2px solid #000;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
thead th,
|
||||||
|
tbody tr td {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
thead{
|
||||||
|
tr {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thead th {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&:first-child{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody tr td {
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #cecbcb;
|
||||||
|
|
||||||
|
&:first-child{
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::last-child{
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thead tr th{
|
||||||
|
&.item{
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
&.rate{
|
||||||
|
width: 18%;
|
||||||
|
}
|
||||||
|
&.quantity{
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
&.total{
|
||||||
|
width: 21%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__conditions{
|
||||||
|
|
||||||
|
&__title{
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
379
server/resources/scss/normalize.scss
vendored
Normal file
379
server/resources/scss/normalize.scss
vendored
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
/* 1 */
|
||||||
|
height: 0;
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none;
|
||||||
|
/* 1 */
|
||||||
|
text-decoration: underline;
|
||||||
|
/* 2 */
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 1em;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
/* 1 */
|
||||||
|
font-size: 100%;
|
||||||
|
/* 1 */
|
||||||
|
line-height: 1.15;
|
||||||
|
/* 1 */
|
||||||
|
margin: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
/* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
color: inherit;
|
||||||
|
/* 2 */
|
||||||
|
display: table;
|
||||||
|
/* 1 */
|
||||||
|
max-width: 100%;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 3 */
|
||||||
|
white-space: normal;
|
||||||
|
/* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 1 */
|
||||||
|
padding: 0;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
/* 1 */
|
||||||
|
outline-offset: -2px;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
/* 1 */
|
||||||
|
font: inherit;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
7
server/resources/views/PaperTemplateLayout.pug
Normal file
7
server/resources/views/PaperTemplateLayout.pug
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
html
|
||||||
|
head
|
||||||
|
title My Site - #{title}
|
||||||
|
block head
|
||||||
|
body
|
||||||
|
div.paper-template
|
||||||
|
block content
|
||||||
67
server/resources/views/modules/estimate-regular.pug
Normal file
67
server/resources/views/modules/estimate-regular.pug
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
extends ../PaperTemplateLayout.pug
|
||||||
|
|
||||||
|
block head
|
||||||
|
style
|
||||||
|
include ../../css/modules/estimate.css
|
||||||
|
|
||||||
|
block content
|
||||||
|
div.estimate
|
||||||
|
div.estimate__header
|
||||||
|
div.organization
|
||||||
|
h3.title #{organizationName}
|
||||||
|
if organizationEmail
|
||||||
|
span.email #{organizationEmail}
|
||||||
|
|
||||||
|
div.paper
|
||||||
|
h1.title #{__("estimate.paper.estimate")}
|
||||||
|
span.email #{saleEstimate.estimateNumber}
|
||||||
|
|
||||||
|
div.estimate__meta
|
||||||
|
div.estimate__meta-item.estimate__meta-item--amount
|
||||||
|
span.label #{__('estimate.paper.due_amount')}
|
||||||
|
span.value #{saleEstimate.formattedAmount}
|
||||||
|
|
||||||
|
div.estimate__meta-item.estimate__meta-item--billed-to
|
||||||
|
span.label #{__("estimate.paper.billed_to")}
|
||||||
|
span.value #{saleEstimate.customer.displayName}
|
||||||
|
|
||||||
|
div.estimate__meta-item.estimate__meta-item--estimate-date
|
||||||
|
span.label #{__("estimate.paper.estimate_date")}
|
||||||
|
span.value #{saleEstimate.formattedEstimateDate}
|
||||||
|
|
||||||
|
if saleEstimate.estimateNumber
|
||||||
|
div.estimate__meta-item.estimate__meta-item--estimate-number
|
||||||
|
span.label #{__("estimate.paper.estimate_number")}
|
||||||
|
span.value #{saleEstimate.estimateNumber}
|
||||||
|
|
||||||
|
div.estimate__meta-item.estimate__meta-item--due-date
|
||||||
|
span.label #{__("estimate.paper.expiration_date")}
|
||||||
|
span.value #{saleEstimate.formattedExpirationDate}
|
||||||
|
|
||||||
|
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
|
||||||
|
each entry in saleEstimate.entries
|
||||||
|
tr
|
||||||
|
td.item
|
||||||
|
div.title=entry.item.name
|
||||||
|
span.description=entry.description
|
||||||
|
td.rate=entry.rate
|
||||||
|
td.quantity=entry.quantity
|
||||||
|
td.total=entry.amount
|
||||||
|
|
||||||
|
if saleEstimate.termsConditions
|
||||||
|
div.estimate__conditions
|
||||||
|
h3 #{__("estimate.paper.conditions_title")}
|
||||||
|
p #{saleEstimate.termsConditions}
|
||||||
|
|
||||||
|
if saleEstimate.note
|
||||||
|
div.estimate__notes
|
||||||
|
h3 #{__("estimate.paper.notes_title")}
|
||||||
|
p #{saleEstimate.note}
|
||||||
68
server/resources/views/modules/invoice-regular.pug
Normal file
68
server/resources/views/modules/invoice-regular.pug
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
extends ../PaperTemplateLayout.pug
|
||||||
|
|
||||||
|
block head
|
||||||
|
style
|
||||||
|
include ../../css/modules/invoice.css
|
||||||
|
|
||||||
|
block content
|
||||||
|
div.invoice
|
||||||
|
div.invoice__header
|
||||||
|
div.organization
|
||||||
|
h3.title #{organizationName}
|
||||||
|
if organizationEmail
|
||||||
|
span.email #{organizationEmail}
|
||||||
|
|
||||||
|
div.paper
|
||||||
|
h1.title #{__("invoice.paper.invoice")}
|
||||||
|
if saleInvoice.invoiceNo
|
||||||
|
span.email #{saleInvoice.invoiceNo}
|
||||||
|
|
||||||
|
div.invoice__meta
|
||||||
|
div.invoice__meta-item.invoice__meta-item--amount
|
||||||
|
span.label #{__('estimate.paper.due_amount')}
|
||||||
|
span.value #{saleInvoice.formattedAmount}
|
||||||
|
|
||||||
|
div.invoice__meta-item.invoice__meta-item--billed-to
|
||||||
|
span.label #{__("invoice.paper.billed_to")}
|
||||||
|
span.value #{saleInvoice.customer.displayName}
|
||||||
|
|
||||||
|
div.invoice__meta-item.invoice__meta-item--invoice-date
|
||||||
|
span.label #{__("invoice.paper.invoice_date")}
|
||||||
|
span.value #{saleInvoice.formattedInvoiceDate}
|
||||||
|
|
||||||
|
if saleInvoice.invoiceNo
|
||||||
|
div.invoice__meta-item.invoice__meta-item--invoice-number
|
||||||
|
span.label #{__("invoice.paper.invoice_number")}
|
||||||
|
span.value #{saleInvoice.invoiceNo}
|
||||||
|
|
||||||
|
div.invoice__meta-item.invoice__meta-item--due-date
|
||||||
|
span.label #{__("invoice.paper.due_date")}
|
||||||
|
span.value #{saleInvoice.formattedDueDate}
|
||||||
|
|
||||||
|
div.invoice__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
|
||||||
|
each entry in saleInvoice.entries
|
||||||
|
tr
|
||||||
|
td.item
|
||||||
|
div.title=entry.item.name
|
||||||
|
span.description=entry.description
|
||||||
|
td.rate=entry.rate
|
||||||
|
td.quantity=entry.quantity
|
||||||
|
td.total=entry.amount
|
||||||
|
|
||||||
|
if saleInvoice.termsConditions
|
||||||
|
div.invoice__conditions
|
||||||
|
h3 #{__("invoice.paper.conditions_title")}
|
||||||
|
p #{saleInvoice.termsConditions}
|
||||||
|
|
||||||
|
if saleInvoice.invoiceMessage
|
||||||
|
div.invoice__notes
|
||||||
|
h3 #{__("invoice.paper.notes_title")}
|
||||||
|
p #{saleInvoice.invoiceMessage}
|
||||||
61
server/resources/views/modules/receipt-regular.pug
Normal file
61
server/resources/views/modules/receipt-regular.pug
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
extends ../PaperTemplateLayout.pug
|
||||||
|
|
||||||
|
block head
|
||||||
|
style
|
||||||
|
include ../../css/modules/receipt.css
|
||||||
|
|
||||||
|
block content
|
||||||
|
div.receipt
|
||||||
|
div.receipt__header
|
||||||
|
div.organization
|
||||||
|
h3.title #{organizationName}
|
||||||
|
if organizationEmail
|
||||||
|
span.email #{organizationEmail}
|
||||||
|
|
||||||
|
div.paper
|
||||||
|
h1.title #{__("receipt.paper.receipt")}
|
||||||
|
span.email #{saleReceipt.receiptNumber}
|
||||||
|
|
||||||
|
div.receipt__meta
|
||||||
|
div.receipt__meta-item.receipt__meta-item--amount
|
||||||
|
span.label #{__('receipt.paper.receipt_amount')}
|
||||||
|
span.value #{saleReceipt.formattedAmount}
|
||||||
|
|
||||||
|
div.receipt__meta-item.receipt__meta-item--billed-to
|
||||||
|
span.label #{__("receipt.paper.billed_to")}
|
||||||
|
span.value #{saleReceipt.customer.displayName}
|
||||||
|
|
||||||
|
div.receipt__meta-item.receipt__meta-item--invoice-date
|
||||||
|
span.label #{__("receipt.paper.receipt_date")}
|
||||||
|
span.value #{saleReceipt.formattedReceiptDate}
|
||||||
|
|
||||||
|
if saleReceipt.receiptNumber
|
||||||
|
div.receipt__meta-item.receipt__meta-item--invoice-number
|
||||||
|
span.label #{__("receipt.paper.receipt_number")}
|
||||||
|
span.value #{saleReceipt.receiptNumber}
|
||||||
|
|
||||||
|
div.receipt__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
|
||||||
|
each entry in saleReceipt.entries
|
||||||
|
tr
|
||||||
|
td.item=entry.item.name
|
||||||
|
td.rate=entry.rate
|
||||||
|
td.quantity=entry.quantity
|
||||||
|
td.total=entry.amount
|
||||||
|
|
||||||
|
if saleReceipt.statement
|
||||||
|
div.receipt__conditions
|
||||||
|
h3 #{__("receipt.paper.statement_title")}
|
||||||
|
p #{saleReceipt.statement}
|
||||||
|
|
||||||
|
if saleReceipt.receiptMessage
|
||||||
|
div.receipt__notes
|
||||||
|
h3 #{__("receipt.paper.notes_title")}
|
||||||
|
p #{saleReceipt.receiptMessage}
|
||||||
117
server/scripts/gulpConfig.js
Normal file
117
server/scripts/gulpConfig.js
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* # Gulp Configuration.
|
||||||
|
* ------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const RESOURCES_PATH = '../resources/';
|
||||||
|
module.exports = {
|
||||||
|
banner: [
|
||||||
|
'/**',
|
||||||
|
' * <%= pkg.name %> - <%= pkg.description %>',
|
||||||
|
' * @version v<%= pkg.version %>',
|
||||||
|
' * @link <%= pkg.homepage %>',
|
||||||
|
' * @author <%= pkg.author %>',
|
||||||
|
' * @license <%= pkg.license %>',
|
||||||
|
'**/',
|
||||||
|
'',
|
||||||
|
].join('\n'),
|
||||||
|
|
||||||
|
// Browser Sync
|
||||||
|
browsersync: {
|
||||||
|
files: ['**/*', '!**.map', '!**.css'], // Exclude map files.
|
||||||
|
notify: false, //
|
||||||
|
open: true, // Set it to false if you don't like the broser window opening automatically.
|
||||||
|
port: 8080, //
|
||||||
|
proxy: 'localhost/customatic', //
|
||||||
|
watchOptions: {
|
||||||
|
debounceDelay: 2000, // This introduces a small delay when watching for file change events to avoid triggering too many reloads
|
||||||
|
},
|
||||||
|
snippetOptions: {
|
||||||
|
whitelist: ['/wp-admin/admin-ajax.php'],
|
||||||
|
blacklist: ['/wp-admin/**'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Style Related.
|
||||||
|
style: {
|
||||||
|
clean: ['style.css', 'style.min.css', 'style-rtl.css', 'style-rtl.min.css'],
|
||||||
|
build: [
|
||||||
|
{
|
||||||
|
src: `${RESOURCES_PATH}/scss/modules/invoice.scss`, // Path to main .scss file.
|
||||||
|
dest: `${RESOURCES_PATH}/css/modules`, // Path to place the compiled CSS file.
|
||||||
|
// sourcemaps: true, // Allow to enable/disable sourcemaps or pass object to configure it.
|
||||||
|
// minify: true, // Allow to enable/disable minify the source.
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: `${RESOURCES_PATH}/scss/modules/estimate.scss`, // Path to main .scss file.
|
||||||
|
dest: `${RESOURCES_PATH}/css/modules`, // Path to place the compiled CSS file.
|
||||||
|
// sourcemaps: true, // Allow to enable/disable sourcemaps or pass object to configure it.
|
||||||
|
// minify: true, // Allow to enable/disable minify the source.
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: `${RESOURCES_PATH}/scss/modules/receipt.scss`, // Path to main .scss file.
|
||||||
|
dest: `${RESOURCES_PATH}/css/modules`, // Path to place the compiled CSS file.
|
||||||
|
// sourcemaps: true, // Allow to enable/disable sourcemaps or pass object to configure it.
|
||||||
|
// minify: true, // Allow to enable/disable minify the source.
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// src: './assets/sass/editor-style.scss',
|
||||||
|
// dest: './assets/css',
|
||||||
|
// sourcemaps: true,
|
||||||
|
// minify: true,
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
rtl: [
|
||||||
|
// RTL builds.
|
||||||
|
{
|
||||||
|
src: './style.css',
|
||||||
|
dest: './', // The source files will be converted and suffixed to `-rtl` in this destination.
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// Browsers you care about for auto-prefixing.
|
||||||
|
autoprefixer: {
|
||||||
|
browsers: [
|
||||||
|
'Android 2.3',
|
||||||
|
'Android >= 4',
|
||||||
|
'Chrome >= 20',
|
||||||
|
'Firefox >= 24',
|
||||||
|
'Explorer >= 9',
|
||||||
|
'iOS >= 6',
|
||||||
|
'Opera >= 12',
|
||||||
|
'Safari >= 6',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// SASS Configuration for all builds.
|
||||||
|
sass: {
|
||||||
|
errLogToConsole: true,
|
||||||
|
// outputStyle: 'compact',
|
||||||
|
},
|
||||||
|
|
||||||
|
// CSS MQ Packer configuration for all builds and style tasks.
|
||||||
|
cssMqpacker: {},
|
||||||
|
|
||||||
|
// CSS nano configuration for all builds.
|
||||||
|
cssnano: {},
|
||||||
|
|
||||||
|
// rtlcss configuration for all builds.
|
||||||
|
rtlcss: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Clean specific files.
|
||||||
|
clean: [
|
||||||
|
'**/.DS_Store',
|
||||||
|
'./assets/js/**/*.min.js',
|
||||||
|
'**/*.map',
|
||||||
|
'**/*.min.css',
|
||||||
|
'assets/js/hypernews.js',
|
||||||
|
],
|
||||||
|
|
||||||
|
// Watch related.
|
||||||
|
watch: {
|
||||||
|
css: ['./assets/sass/**/*'],
|
||||||
|
js: ['assets/js/**/*.js', '!assets/js/**/*.min.js'],
|
||||||
|
images: ['./assets/images/**/*'],
|
||||||
|
},
|
||||||
|
};
|
||||||
15
server/scripts/gulpfile.js
Normal file
15
server/scripts/gulpfile.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const gulp = require('gulp');
|
||||||
|
const sass = require('sass');
|
||||||
|
const gulpSass = require('gulp-sass')(sass); // Gulp pluign for Sass compilation.
|
||||||
|
const mergeStream = require('merge-stream');
|
||||||
|
const config = require('./gulpConfig');
|
||||||
|
|
||||||
|
gulp.task('styles', () => {
|
||||||
|
const builds = config.style.build.map((build) => {
|
||||||
|
return gulp
|
||||||
|
.src(build.src)
|
||||||
|
.pipe(gulpSass(config.style.sass))
|
||||||
|
.pipe(gulp.dest(build.dest));
|
||||||
|
});
|
||||||
|
return mergeStream(builds);
|
||||||
|
});
|
||||||
@@ -111,8 +111,6 @@ export default class BaseController {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async middleware.
|
* Async middleware.
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
@@ -129,4 +127,14 @@ export default class BaseController {
|
|||||||
protected accepts(req) {
|
protected accepts(req) {
|
||||||
return accepts(req);
|
return accepts(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {string[]} types
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected acceptTypes(req: Request, types: string[]) {
|
||||||
|
return this.accepts(req).types(types);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,11 +165,12 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
const paymentReceive: IPaymentReceiveDTO = this.matchedBodyData(req);
|
const paymentReceive: IPaymentReceiveDTO = this.matchedBodyData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const storedPaymentReceive = await this.paymentReceiveService.createPaymentReceive(
|
const storedPaymentReceive =
|
||||||
tenantId,
|
await this.paymentReceiveService.createPaymentReceive(
|
||||||
paymentReceive,
|
tenantId,
|
||||||
user
|
paymentReceive,
|
||||||
);
|
user
|
||||||
|
);
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
id: storedPaymentReceive.id,
|
id: storedPaymentReceive.id,
|
||||||
message: 'The payment receive has been created successfully.',
|
message: 'The payment receive has been created successfully.',
|
||||||
@@ -247,11 +248,13 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
const { id: paymentReceiveId } = req.params;
|
const { id: paymentReceiveId } = req.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const invoices = await this.paymentReceiveService.getPaymentReceiveInvoices(
|
const saleInvoices =
|
||||||
tenantId,
|
await this.paymentReceiveService.getPaymentReceiveInvoices(
|
||||||
paymentReceiveId
|
tenantId,
|
||||||
);
|
paymentReceiveId
|
||||||
return res.status(200).send({ sale_invoices: invoices });
|
);
|
||||||
|
|
||||||
|
return res.status(200).send(this.transfromToResponse({ saleInvoices }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -274,17 +277,11 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {
|
const { paymentReceives, pagination, filterMeta } =
|
||||||
paymentReceives,
|
await this.paymentReceiveService.listPaymentReceives(tenantId, filter);
|
||||||
pagination,
|
|
||||||
filterMeta,
|
|
||||||
} = await this.paymentReceiveService.listPaymentReceives(
|
|
||||||
tenantId,
|
|
||||||
filter
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
payment_receives: paymentReceives,
|
payment_receives: this.transfromToResponse(paymentReceives),
|
||||||
pagination: this.transfromToResponse(pagination),
|
pagination: this.transfromToResponse(pagination),
|
||||||
filter_meta: this.transfromToResponse(filterMeta),
|
filter_meta: this.transfromToResponse(filterMeta),
|
||||||
});
|
});
|
||||||
@@ -334,14 +331,12 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
const { id: paymentReceiveId } = req.params;
|
const { id: paymentReceiveId } = req.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {
|
const { paymentReceive, entries } =
|
||||||
paymentReceive,
|
await this.PaymentReceivesPages.getPaymentReceiveEditPage(
|
||||||
entries,
|
tenantId,
|
||||||
} = await this.PaymentReceivesPages.getPaymentReceiveEditPage(
|
paymentReceiveId,
|
||||||
tenantId,
|
user
|
||||||
paymentReceiveId,
|
);
|
||||||
user
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
payment_receive: this.transfromToResponse({ ...paymentReceive }),
|
payment_receive: this.transfromToResponse({ ...paymentReceive }),
|
||||||
@@ -442,9 +437,10 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
type: 'INVOICES_NOT_DELIVERED_YET',
|
type: 'INVOICES_NOT_DELIVERED_YET',
|
||||||
code: 200,
|
code: 200,
|
||||||
data: {
|
data: {
|
||||||
not_delivered_invoices_ids: error.payload.notDeliveredInvoices.map(
|
not_delivered_invoices_ids:
|
||||||
(invoice) => invoice.id
|
error.payload.notDeliveredInvoices.map(
|
||||||
),
|
(invoice) => invoice.id
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -7,7 +7,12 @@ import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
|||||||
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
||||||
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
|
import SaleEstimatesPdfService from 'services/Sales/Estimates/SaleEstimatesPdf';
|
||||||
|
|
||||||
|
const ACCEPT_TYPE = {
|
||||||
|
APPLICATION_PDF: 'application/pdf',
|
||||||
|
APPLICATION_JSON: 'application/json',
|
||||||
|
};
|
||||||
@Service()
|
@Service()
|
||||||
export default class SalesEstimatesController extends BaseController {
|
export default class SalesEstimatesController extends BaseController {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -16,6 +21,9 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
@Inject()
|
@Inject()
|
||||||
dynamicListService: DynamicListingService;
|
dynamicListService: DynamicListingService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
saleEstimatesPdf: SaleEstimatesPdfService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
*/
|
*/
|
||||||
@@ -135,7 +143,7 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||||
query('page').optional().isNumeric().toInt(),
|
query('page').optional().isNumeric().toInt(),
|
||||||
query('page_size').optional().isNumeric().toInt(),
|
query('page_size').optional().isNumeric().toInt(),
|
||||||
query('search_keyword').optional({ nullable: true }).isString().trim()
|
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,8 +300,25 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
tenantId,
|
tenantId,
|
||||||
estimateId
|
estimateId
|
||||||
);
|
);
|
||||||
|
// Response formatter.
|
||||||
return res.status(200).send({ estimate });
|
res.format({
|
||||||
|
// PDF content type.
|
||||||
|
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
|
||||||
|
const pdfContent = await this.saleEstimatesPdf.saleEstimatePdf(
|
||||||
|
tenantId,
|
||||||
|
estimate
|
||||||
|
);
|
||||||
|
res.set({
|
||||||
|
'Content-Type': 'application/pdf',
|
||||||
|
'Content-Length': pdfContent.length,
|
||||||
|
});
|
||||||
|
res.send(pdfContent);
|
||||||
|
},
|
||||||
|
// JSON content type.
|
||||||
|
default: () => {
|
||||||
|
return res.status(200).send(this.transfromToResponse({ estimate }));
|
||||||
|
},
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -318,10 +343,16 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
const { salesEstimates, pagination, filterMeta } =
|
const { salesEstimates, pagination, filterMeta } =
|
||||||
await this.saleEstimateService.estimatesList(tenantId, filter);
|
await this.saleEstimateService.estimatesList(tenantId, filter);
|
||||||
|
|
||||||
return res.status(200).send({
|
res.format({
|
||||||
sales_estimates: this.transfromToResponse(salesEstimates),
|
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
|
||||||
pagination,
|
return res.status(200).send(
|
||||||
filter_meta: this.transfromToResponse(filterMeta),
|
this.transfromToResponse({
|
||||||
|
salesEstimates,
|
||||||
|
pagination,
|
||||||
|
filterMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
|
|||||||
@@ -8,7 +8,12 @@ import ItemsService from 'services/Items/ItemsService';
|
|||||||
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
import { ISaleInvoiceDTO, ISaleInvoiceCreateDTO } from 'interfaces';
|
import { ISaleInvoiceDTO, ISaleInvoiceCreateDTO } from 'interfaces';
|
||||||
|
import SaleInvoicePdf from 'services/Sales/SaleInvoicePdf';
|
||||||
|
|
||||||
|
const ACCEPT_TYPE = {
|
||||||
|
APPLICATION_PDF: 'application/pdf',
|
||||||
|
APPLICATION_JSON: 'application/json',
|
||||||
|
};
|
||||||
@Service()
|
@Service()
|
||||||
export default class SaleInvoicesController extends BaseController {
|
export default class SaleInvoicesController extends BaseController {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -20,6 +25,9 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
@Inject()
|
@Inject()
|
||||||
dynamicListService: DynamicListingService;
|
dynamicListService: DynamicListingService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
saleInvoicePdf: SaleInvoicePdf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
*/
|
*/
|
||||||
@@ -254,8 +262,8 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the sale invoice with associated entries.
|
* Retrieve the sale invoice with associated entries.
|
||||||
* @param {Request} req
|
* @param {Request} req - Request object.
|
||||||
* @param {Response} res
|
* @param {Response} res - Response object.
|
||||||
*/
|
*/
|
||||||
async getSaleInvoice(req: Request, res: Response, next: NextFunction) {
|
async getSaleInvoice(req: Request, res: Response, next: NextFunction) {
|
||||||
const { id: saleInvoiceId } = req.params;
|
const { id: saleInvoiceId } = req.params;
|
||||||
@@ -267,7 +275,25 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
user
|
user
|
||||||
);
|
);
|
||||||
return res.status(200).send({ sale_invoice: saleInvoice });
|
// Response formatter.
|
||||||
|
res.format({
|
||||||
|
// PDF content type.
|
||||||
|
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
|
||||||
|
const pdfContent = await this.saleInvoicePdf.saleInvoicePdf(
|
||||||
|
tenantId,
|
||||||
|
saleInvoice
|
||||||
|
);
|
||||||
|
res.set({
|
||||||
|
'Content-Type': 'application/pdf',
|
||||||
|
'Content-Length': pdfContent.length,
|
||||||
|
});
|
||||||
|
res.send(pdfContent);
|
||||||
|
},
|
||||||
|
// JSON content type.
|
||||||
|
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
|
||||||
|
return res.status(200).send(this.transfromToResponse({ saleInvoice }));
|
||||||
|
},
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -296,7 +322,7 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
await this.saleInvoiceService.salesInvoicesList(tenantId, filter);
|
await this.saleInvoiceService.salesInvoicesList(tenantId, filter);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
sales_invoices: salesInvoices,
|
sales_invoices: this.transfromToResponse(salesInvoices),
|
||||||
pagination: this.transfromToResponse(pagination),
|
pagination: this.transfromToResponse(pagination),
|
||||||
filter_meta: this.transfromToResponse(filterMeta),
|
filter_meta: this.transfromToResponse(filterMeta),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { check, param, query } from 'express-validator';
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import SaleReceiptService from 'services/Sales/SalesReceipts';
|
import SaleReceiptService from 'services/Sales/SalesReceipts';
|
||||||
|
import SaleReceiptsPdfService from 'services/Sales/Receipts/SaleReceiptsPdfService';
|
||||||
import BaseController from '../BaseController';
|
import BaseController from '../BaseController';
|
||||||
import { ISaleReceiptDTO } from 'interfaces/SaleReceipt';
|
import { ISaleReceiptDTO } from 'interfaces/SaleReceipt';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
@@ -13,6 +14,9 @@ export default class SalesReceiptsController extends BaseController {
|
|||||||
@Inject()
|
@Inject()
|
||||||
saleReceiptService: SaleReceiptService;
|
saleReceiptService: SaleReceiptService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
saleReceiptsPdf: SaleReceiptsPdfService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
dynamicListService: DynamicListingService;
|
dynamicListService: DynamicListingService;
|
||||||
|
|
||||||
@@ -239,17 +243,13 @@ export default class SalesReceiptsController extends BaseController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {
|
const { salesReceipts, pagination, filterMeta } =
|
||||||
salesReceipts,
|
await this.saleReceiptService.salesReceiptsList(tenantId, filter);
|
||||||
pagination,
|
|
||||||
filterMeta,
|
|
||||||
} = await this.saleReceiptService.salesReceiptsList(tenantId, filter);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
const response = this.transfromToResponse({
|
||||||
sale_receipts: salesReceipts,
|
salesReceipts, pagination, filterMeta
|
||||||
pagination: this.transfromToResponse(pagination),
|
|
||||||
filter_meta: this.transfromToResponse(filterMeta),
|
|
||||||
});
|
});
|
||||||
|
return res.status(200).send(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -271,9 +271,22 @@ export default class SalesReceiptsController extends BaseController {
|
|||||||
saleReceiptId
|
saleReceiptId
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
res.format({
|
||||||
sale_receipt: saleReceipt,
|
'application/pdf': async () => {
|
||||||
});
|
const pdfContent = await this.saleReceiptsPdf.saleReceiptPdf(
|
||||||
|
tenantId,
|
||||||
|
saleReceipt
|
||||||
|
);
|
||||||
|
res.set({
|
||||||
|
'Content-Type': 'application/pdf',
|
||||||
|
'Content-Length': pdfContent.length,
|
||||||
|
});
|
||||||
|
res.send(pdfContent);
|
||||||
|
},
|
||||||
|
'application/json': () => {
|
||||||
|
return res.status(200).send(this.transfromToResponse({ saleReceipt }));
|
||||||
|
}
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,12 +42,14 @@ import Subscription from 'api/controllers/Subscription';
|
|||||||
import Licenses from 'api/controllers/Subscription/Licenses';
|
import Licenses from 'api/controllers/Subscription/Licenses';
|
||||||
import InventoryAdjustments from 'api/controllers/Inventory/InventoryAdjustments';
|
import InventoryAdjustments from 'api/controllers/Inventory/InventoryAdjustments';
|
||||||
import Setup from 'api/controllers/Setup';
|
import Setup from 'api/controllers/Setup';
|
||||||
|
import asyncRenderMiddleware from './middleware/AsyncRenderMiddleware';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const app = Router();
|
const app = Router();
|
||||||
|
|
||||||
// - Global routes.
|
// - Global routes.
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
app.use(asyncRenderMiddleware);
|
||||||
app.use(i18n.init);
|
app.use(i18n.init);
|
||||||
app.use(I18nMiddleware);
|
app.use(I18nMiddleware);
|
||||||
|
|
||||||
|
|||||||
23
server/src/api/middleware/AsyncRenderMiddleware.ts
Normal file
23
server/src/api/middleware/AsyncRenderMiddleware.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
|
const asyncRender = (app) => (path: string, attributes = {}) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
app.render(path, attributes, (error, data) => {
|
||||||
|
if (error) { reject(error); }
|
||||||
|
|
||||||
|
resolve(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects `asyncRender` method to response object.
|
||||||
|
* @param {Request} req Express req Object
|
||||||
|
* @param {Response} res Express res Object
|
||||||
|
* @param {NextFunction} next Express next Function
|
||||||
|
*/
|
||||||
|
const asyncRenderMiddleware = (req: Request, res: Response, next: Function) => {
|
||||||
|
res.asyncRender = asyncRender(req.app);
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default asyncRenderMiddleware;
|
||||||
@@ -162,6 +162,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puppeteer remote browserless connection.
|
||||||
|
*/
|
||||||
|
puppeteer: {
|
||||||
|
browserWSEndpoint: process.env.BROWSER_WS_ENDPOINT,
|
||||||
|
},
|
||||||
|
|
||||||
protocol: '',
|
protocol: '',
|
||||||
hostname: '',
|
hostname: '',
|
||||||
scheduleComputeItemCost: 'in 5 seconds'
|
scheduleComputeItemCost: 'in 5 seconds'
|
||||||
|
|||||||
64
server/src/lib/Transformer/Transformer.ts
Normal file
64
server/src/lib/Transformer/Transformer.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import moment from "moment";
|
||||||
|
import { isEmpty, isObject, isUndefined } from 'lodash';
|
||||||
|
|
||||||
|
export class Transformer {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected includeAttributes = (): string[] => {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public transform = (object: any) => {
|
||||||
|
if (Array.isArray(object)) {
|
||||||
|
return object.map(this.getTransformation);
|
||||||
|
} else if (isObject(object)) {
|
||||||
|
return this.getTransformation(object);
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected getTransformation = (item) => {
|
||||||
|
const attributes = this.getIncludeAttributesTransformed(item);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...!isUndefined(item.toJSON) ? item.toObject() : item,
|
||||||
|
...attributes
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected getIncludeAttributesTransformed = (item) => {
|
||||||
|
const attributes = this.includeAttributes();
|
||||||
|
|
||||||
|
return attributes
|
||||||
|
.filter((attribute) => !isUndefined(this[attribute]))
|
||||||
|
.reduce((acc, attribute: string) => {
|
||||||
|
acc[attribute] = this[attribute](item);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param date
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected formatDate(date) {
|
||||||
|
return date ? moment(date).format('YYYY/MM/DD') : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,11 +10,16 @@ import AgendashController from 'api/controllers/Agendash';
|
|||||||
import ConvertEmptyStringsToNull from 'api/middleware/ConvertEmptyStringsToNull';
|
import ConvertEmptyStringsToNull from 'api/middleware/ConvertEmptyStringsToNull';
|
||||||
import RateLimiterMiddleware from 'api/middleware/RateLimiterMiddleware'
|
import RateLimiterMiddleware from 'api/middleware/RateLimiterMiddleware'
|
||||||
import config from 'config';
|
import config from 'config';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export default ({ app }) => {
|
export default ({ app }) => {
|
||||||
// Express configuration.
|
// Express configuration.
|
||||||
app.set('port', 3000);
|
app.set('port', 3000);
|
||||||
|
|
||||||
|
// Template engine configuration.
|
||||||
|
app.set('views', path.join(__dirname, '../resources/views'));
|
||||||
|
app.set('view engine', 'pug');
|
||||||
|
|
||||||
// Helmet helps you secure your Express apps by setting various HTTP headers.
|
// Helmet helps you secure your Express apps by setting various HTTP headers.
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
|
|
||||||
|
|||||||
@@ -159,5 +159,32 @@
|
|||||||
"Liabilities and Equity": "Liabilities and Equity",
|
"Liabilities and Equity": "Liabilities and Equity",
|
||||||
"Closing balance": "Closing balance",
|
"Closing balance": "Closing balance",
|
||||||
"Opening Balance": "Opening balance",
|
"Opening Balance": "Opening balance",
|
||||||
"Total {{accountName}}": "Total {{accountName}}"
|
"Total {{accountName}}": "Total {{accountName}}",
|
||||||
|
"invoice.paper.invoice": "Invoice",
|
||||||
|
"invoice.paper.billed_to": "Billed to",
|
||||||
|
"invoice.paper.invoice_date": "Invoice date",
|
||||||
|
"invoice.paper.invoice_number": "Invoice No.",
|
||||||
|
"invoice.paper.due_date": "Due date",
|
||||||
|
"invoice.paper.conditions_title": "Conditions & terms",
|
||||||
|
"invoice.paper.notes_title": "Notes",
|
||||||
|
"item_entry.paper.item_name": "Item name",
|
||||||
|
"item_entry.paper.rate": "Rate",
|
||||||
|
"item_entry.paper.quantity": "Quantity",
|
||||||
|
"item_entry.paper.total": "Total",
|
||||||
|
"estimate.paper.estimate": "Estimate",
|
||||||
|
"estimate.paper.billed_to": "Billed to",
|
||||||
|
"estimate.paper.estimate_date": "Estimate date",
|
||||||
|
"estimate.paper.estimate_number": "Estimate number",
|
||||||
|
"estimate.paper.expiration_date": "Expiration date",
|
||||||
|
"estimate.paper.conditions_title": "Conditions & terms",
|
||||||
|
"estimate.paper.notes_title": "Notes",
|
||||||
|
"estimate.paper.due_amount": "Due amount",
|
||||||
|
"receipt.paper.receipt": "Receipt",
|
||||||
|
"receipt.paper.billed_to": "Billed to",
|
||||||
|
"receipt.paper.receipt_date": "Receipt date",
|
||||||
|
"receipt.paper.receipt_number": "Receipt number",
|
||||||
|
"receipt.paper.expiration_date": "Expiration date",
|
||||||
|
"receipt.paper.conditions_title": "Conditions & terms",
|
||||||
|
"receipt.paper.notes_title": "Notes",
|
||||||
|
"receipt.paper.receipt_amount": "Receipt amount"
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Model, mixin } from 'objection';
|
import { Model, mixin } from 'objection';
|
||||||
import { snakeCase } from 'lodash';
|
import { snakeCase, transform } from 'lodash';
|
||||||
import { mapKeysDeep } from 'utils';
|
import { mapKeysDeep } from 'utils';
|
||||||
import PaginationQueryBuilder from 'models/Pagination';
|
import PaginationQueryBuilder from 'models/Pagination';
|
||||||
import DateSession from 'models/DateSession';
|
import DateSession from 'models/DateSession';
|
||||||
@@ -47,4 +47,9 @@ export default class ModelBase extends mixin(Model, [DateSession]) {
|
|||||||
static relationBindKnex(model) {
|
static relationBindKnex(model) {
|
||||||
return this.knexBinded ? model.bindKnex(this.knexBinded) : model;
|
return this.knexBinded ? model.bindKnex(this.knexBinded) : model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toObject(opt) {
|
||||||
|
const parsedJson = super.$formatJson(this, opt);
|
||||||
|
return parsedJson;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
server/src/services/PDF/PdfService.ts
Normal file
26
server/src/services/PDF/PdfService.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
import config from 'config';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class PdfService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pdf document.
|
||||||
|
* @param content
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async pdfDocument(content: string) {
|
||||||
|
const browser = await puppeteer.connect({
|
||||||
|
browserWSEndpoint: config.puppeteer.browserWSEndpoint,
|
||||||
|
});
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.setContent(content);
|
||||||
|
|
||||||
|
const pdf = await page.pdf({ format: 'a4' });
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
|
||||||
|
return pdf;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import { IBill, IBillPayment } from 'interfaces';
|
||||||
|
import { Transformer } from 'lib/Transformer/Transformer';
|
||||||
|
import { formatNumber } from 'utils';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class BillPaymentTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
protected includeAttributes = (): string[] => {
|
||||||
|
return ['formattedPaymentDate', 'formattedAmount'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice date.
|
||||||
|
* @param {IBill} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedPaymentDate = (billPayment: IBillPayment): string => {
|
||||||
|
return this.formatDate(billPayment.paymentDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted bill amount.
|
||||||
|
* @param {IBill} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (billPayment: IBillPayment): string => {
|
||||||
|
return formatNumber(billPayment.amount, {
|
||||||
|
currencyCode: billPayment.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import { entriesAmountDiff, formatDateFields } from 'utils';
|
|||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
import { ACCOUNT_TYPE } from 'data/AccountTypes';
|
import { ACCOUNT_TYPE } from 'data/AccountTypes';
|
||||||
import VendorsService from 'services/Contacts/VendorsService';
|
import VendorsService from 'services/Contacts/VendorsService';
|
||||||
|
import BillPaymentTransformer from './BillPaymentTransformer';
|
||||||
import { ERRORS } from './constants';
|
import { ERRORS } from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,6 +58,9 @@ export default class BillPaymentsService implements IBillPaymentsService {
|
|||||||
@Inject('logger')
|
@Inject('logger')
|
||||||
logger: any;
|
logger: any;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
billPaymentTransformer: BillPaymentTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate whether the bill payment vendor exists on the storage.
|
* Validate whether the bill payment vendor exists on the storage.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
@@ -546,7 +550,7 @@ export default class BillPaymentsService implements IBillPaymentsService {
|
|||||||
if (!billPayment) {
|
if (!billPayment) {
|
||||||
throw new ServiceError(ERRORS.PAYMENT_MADE_NOT_FOUND);
|
throw new ServiceError(ERRORS.PAYMENT_MADE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return billPayment;
|
return this.billPaymentTransformer.transform(billPayment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -680,7 +684,7 @@ export default class BillPaymentsService implements IBillPaymentsService {
|
|||||||
.pagination(filter.page - 1, filter.pageSize);
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
billPayments: results,
|
billPayments: this.billPaymentTransformer.transform(results),
|
||||||
pagination,
|
pagination,
|
||||||
filterMeta: dynamicList.getResponseMeta(),
|
filterMeta: dynamicList.getResponseMeta(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import JournalPosterService from 'services/Sales/JournalPosterService';
|
|||||||
import VendorsService from 'services/Contacts/VendorsService';
|
import VendorsService from 'services/Contacts/VendorsService';
|
||||||
import { ERRORS } from './constants';
|
import { ERRORS } from './constants';
|
||||||
import EntriesService from 'services/Entries';
|
import EntriesService from 'services/Entries';
|
||||||
|
import PurchaseInvoiceTransfromer from './PurchaseInvoices/PurchaseInvoiceTransformer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vendor bills services.
|
* Vendor bills services.
|
||||||
@@ -78,6 +79,9 @@ export default class BillsService
|
|||||||
@Inject()
|
@Inject()
|
||||||
entriesService: EntriesService;
|
entriesService: EntriesService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
purchaseInvoiceTransformer: PurchaseInvoiceTransfromer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates whether the vendor is exist.
|
* Validates whether the vendor is exist.
|
||||||
* @async
|
* @async
|
||||||
@@ -568,7 +572,7 @@ export default class BillsService
|
|||||||
.pagination(filter.page - 1, filter.pageSize);
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bills: results,
|
bills: this.purchaseInvoiceTransformer.transform(results),
|
||||||
pagination,
|
pagination,
|
||||||
filterMeta: dynamicFilter.getResponseMeta(),
|
filterMeta: dynamicFilter.getResponseMeta(),
|
||||||
};
|
};
|
||||||
@@ -616,7 +620,7 @@ export default class BillsService
|
|||||||
if (!bill) {
|
if (!bill) {
|
||||||
throw new ServiceError(ERRORS.BILL_NOT_FOUND);
|
throw new ServiceError(ERRORS.BILL_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return bill;
|
return this.purchaseInvoiceTransformer.transform(bill);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import { IBill } from 'interfaces';
|
||||||
|
import { Transformer } from 'lib/Transformer/Transformer';
|
||||||
|
import { formatNumber } from 'utils';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class PurchaseInvoiceTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
protected includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formattedBillDate',
|
||||||
|
'formattedDueDate',
|
||||||
|
'formattedAmount',
|
||||||
|
'formattedPaymentAmount',
|
||||||
|
'formattedDueAmount',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice date.
|
||||||
|
* @param {IBill} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedBillDate = (bill: IBill): string => {
|
||||||
|
return this.formatDate(bill.billDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice date.
|
||||||
|
* @param {IBill} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedDueDate = (bill: IBill): string => {
|
||||||
|
return this.formatDate(bill.dueDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted bill amount.
|
||||||
|
* @param {IBill} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (bill): string => {
|
||||||
|
return formatNumber(bill.amount, { currencyCode: bill.currencyCode });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted bill amount.
|
||||||
|
* @param {IBill} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedPaymentAmount = (bill): string => {
|
||||||
|
return formatNumber(bill.paymentAmount, { currencyCode: bill.currencyCode});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted bill amount.
|
||||||
|
* @param {IBill} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDueAmount = (bill): string => {
|
||||||
|
return formatNumber(bill.dueAmount, { currencyCode: bill.currencyCode });
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import { ISaleEstimate } from 'interfaces';
|
||||||
|
import { Transformer } from 'lib/Transformer/Transformer';
|
||||||
|
import { formatNumber } from 'utils';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class SaleEstimateTransfromer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
protected includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formattedAmount',
|
||||||
|
'formattedEstimateDate',
|
||||||
|
'formattedExpirationDate',
|
||||||
|
'formattedDeliveredAtDate',
|
||||||
|
'formattedApprovedAtDate',
|
||||||
|
'formattedRejectedAtDate',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted estimate date.
|
||||||
|
* @param {ISaleEstimate} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedEstimateDate = (estimate: ISaleEstimate): string => {
|
||||||
|
return this.formatDate(estimate.estimateDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted estimate date.
|
||||||
|
* @param {ISaleEstimate} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedExpirationDate = (estimate: ISaleEstimate): string => {
|
||||||
|
return this.formatDate(estimate.expirationDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted estimate date.
|
||||||
|
* @param {ISaleEstimate} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedDeliveredAtDate = (estimate: ISaleEstimate): string => {
|
||||||
|
return this.formatDate(estimate.deliveredAt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted estimate date.
|
||||||
|
* @param {ISaleEstimate} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedApprovedAtDate = (estimate: ISaleEstimate): string => {
|
||||||
|
return this.formatDate(estimate.approvedAt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted estimate date.
|
||||||
|
* @param {ISaleEstimate} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedRejectedAtDate = (estimate: ISaleEstimate): string => {
|
||||||
|
return this.formatDate(estimate.rejectedAt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice amount.
|
||||||
|
* @param {ISaleEstimate} estimate
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (estimate: ISaleEstimate): string => {
|
||||||
|
return formatNumber(estimate.amount, {
|
||||||
|
currencyCode: estimate.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
43
server/src/services/Sales/Estimates/SaleEstimatesPdf.ts
Normal file
43
server/src/services/Sales/Estimates/SaleEstimatesPdf.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import PdfService from 'services/PDF/PdfService';
|
||||||
|
import { templateRender } from 'utils';
|
||||||
|
import HasTenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class SaleEstimatesPdf {
|
||||||
|
@Inject()
|
||||||
|
pdfService: PdfService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve sale invoice pdf content.
|
||||||
|
* @param {} saleInvoice -
|
||||||
|
*/
|
||||||
|
async saleEstimatePdf(tenantId: number, saleEstimate) {
|
||||||
|
const i18n = this.tenancy.i18n(tenantId);
|
||||||
|
const settings = this.tenancy.settings(tenantId);
|
||||||
|
|
||||||
|
const organizationName = settings.get({
|
||||||
|
group: 'organization',
|
||||||
|
key: 'name',
|
||||||
|
});
|
||||||
|
const organizationEmail = settings.get({
|
||||||
|
group: 'organization',
|
||||||
|
key: 'email',
|
||||||
|
});
|
||||||
|
const htmlContent = templateRender('modules/estimate-regular', {
|
||||||
|
saleEstimate,
|
||||||
|
organizationName,
|
||||||
|
organizationEmail,
|
||||||
|
...i18n,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(htmlContent, 'XXX');
|
||||||
|
|
||||||
|
const pdfContent = await this.pdfService.pdfDocument(htmlContent);
|
||||||
|
|
||||||
|
return pdfContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import { IPaymentReceive } from 'interfaces';
|
||||||
|
import { Transformer } from 'lib/Transformer/Transformer';
|
||||||
|
import { formatNumber } from 'utils';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class PaymentReceiveTransfromer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to payment receive object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
protected includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formattedPaymentDate',
|
||||||
|
'formattedAmount',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted payment receive date.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedPaymentDate = (payment: IPaymentReceive): string => {
|
||||||
|
return this.formatDate(payment.paymentDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted payment amount.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (payment: IPaymentReceive): string => {
|
||||||
|
return formatNumber(payment.amount, { currencyCode: payment.currencyCode });
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ import JournalCommands from 'services/Accounting/JournalCommands';
|
|||||||
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from 'data/AccountTypes';
|
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from 'data/AccountTypes';
|
||||||
import AutoIncrementOrdersService from '../AutoIncrementOrdersService';
|
import AutoIncrementOrdersService from '../AutoIncrementOrdersService';
|
||||||
import { ERRORS } from './constants';
|
import { ERRORS } from './constants';
|
||||||
|
import PaymentReceiveTransfromer from './PaymentReceiveTransformer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment receive service.
|
* Payment receive service.
|
||||||
@@ -68,6 +69,9 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
|
|||||||
@EventDispatcher()
|
@EventDispatcher()
|
||||||
eventDispatcher: EventDispatcherInterface;
|
eventDispatcher: EventDispatcherInterface;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
paymentReceiveTransformer: PaymentReceiveTransfromer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the payment receive number existance.
|
* Validates the payment receive number existance.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
@@ -584,7 +588,7 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
|
|||||||
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
|
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return paymentReceive;
|
return this.paymentReceiveTransformer.transform(paymentReceive);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -661,7 +665,7 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paymentReceives: results,
|
paymentReceives: this.paymentReceiveTransformer.transform(results),
|
||||||
pagination,
|
pagination,
|
||||||
filterMeta: dynamicList.getResponseMeta(),
|
filterMeta: dynamicList.getResponseMeta(),
|
||||||
};
|
};
|
||||||
|
|||||||
44
server/src/services/Sales/Receipts/SaleReceiptTransformer.ts
Normal file
44
server/src/services/Sales/Receipts/SaleReceiptTransformer.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import { ISaleReceipt } from 'interfaces';
|
||||||
|
import { Transformer } from 'lib/Transformer/Transformer';
|
||||||
|
import { formatNumber } from 'utils';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class SaleReceiptTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
protected includeAttributes = (): string[] => {
|
||||||
|
return ['formattedAmount', 'formattedReceiptDate', 'formattedClosedAtDate'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted receipt date.
|
||||||
|
* @param {ISaleReceipt} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedReceiptDate = (receipt: ISaleReceipt): string => {
|
||||||
|
return this.formatDate(receipt.receiptDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted estimate closed at date.
|
||||||
|
* @param {ISaleReceipt} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedClosedAtDate = (receipt: ISaleReceipt): string => {
|
||||||
|
return this.formatDate(receipt.closedAt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice amount.
|
||||||
|
* @param {ISaleReceipt} estimate
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (receipt: ISaleReceipt): string => {
|
||||||
|
return formatNumber(receipt.amount, {
|
||||||
|
currencyCode: receipt.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
41
server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts
Normal file
41
server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import PdfService from 'services/PDF/PdfService';
|
||||||
|
import { templateRender } from 'utils';
|
||||||
|
import HasTenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class SaleReceiptsPdf {
|
||||||
|
@Inject()
|
||||||
|
pdfService: PdfService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve sale invoice pdf content.
|
||||||
|
* @param {} saleInvoice -
|
||||||
|
*/
|
||||||
|
async saleReceiptPdf(tenantId: number, saleReceipt) {
|
||||||
|
const i18n = this.tenancy.i18n(tenantId);
|
||||||
|
const settings = this.tenancy.settings(tenantId);
|
||||||
|
|
||||||
|
const organizationName = settings.get({
|
||||||
|
group: 'organization',
|
||||||
|
key: 'name',
|
||||||
|
});
|
||||||
|
const organizationEmail = settings.get({
|
||||||
|
group: 'organization',
|
||||||
|
key: 'email',
|
||||||
|
});
|
||||||
|
|
||||||
|
const htmlContent = templateRender('modules/receipt-regular', {
|
||||||
|
saleReceipt,
|
||||||
|
organizationEmail,
|
||||||
|
organizationName,
|
||||||
|
...i18n,
|
||||||
|
});
|
||||||
|
const pdfContent = await this.pdfService.pdfDocument(htmlContent);
|
||||||
|
|
||||||
|
return pdfContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,13 @@
|
|||||||
|
export const ERRORS = {
|
||||||
|
SALE_RECEIPT_NOT_FOUND: 'SALE_RECEIPT_NOT_FOUND',
|
||||||
|
DEPOSIT_ACCOUNT_NOT_FOUND: 'DEPOSIT_ACCOUNT_NOT_FOUND',
|
||||||
|
DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET: 'DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET',
|
||||||
|
SALE_RECEIPT_NUMBER_NOT_UNIQUE: 'SALE_RECEIPT_NUMBER_NOT_UNIQUE',
|
||||||
|
SALE_RECEIPT_IS_ALREADY_CLOSED: 'SALE_RECEIPT_IS_ALREADY_CLOSED',
|
||||||
|
SALE_RECEIPT_NO_IS_REQUIRED: 'SALE_RECEIPT_NO_IS_REQUIRED',
|
||||||
|
CUSTOMER_HAS_SALES_INVOICES: 'CUSTOMER_HAS_SALES_INVOICES',
|
||||||
|
};
|
||||||
|
|
||||||
export const DEFAULT_VIEW_COLUMNS = [];
|
export const DEFAULT_VIEW_COLUMNS = [];
|
||||||
export const DEFAULT_VIEWS = [
|
export const DEFAULT_VIEWS = [
|
||||||
{
|
{
|
||||||
|
|||||||
35
server/src/services/Sales/SaleInvoicePdf.ts
Normal file
35
server/src/services/Sales/SaleInvoicePdf.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import PdfService from 'services/PDF/PdfService';
|
||||||
|
import { templateRender } from 'utils';
|
||||||
|
import HasTenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class SaleInvoicePdf {
|
||||||
|
@Inject()
|
||||||
|
pdfService: PdfService
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve sale invoice pdf content.
|
||||||
|
* @param {} saleInvoice -
|
||||||
|
*/
|
||||||
|
async saleInvoicePdf(tenantId: number, saleInvoice) {
|
||||||
|
const i18n = this.tenancy.i18n(tenantId);
|
||||||
|
const settings = this.tenancy.settings(tenantId);
|
||||||
|
|
||||||
|
const organizationName = settings.get({ group: 'organization', key: 'name' });
|
||||||
|
const organizationEmail = settings.get({ group: 'organization', key: 'email' });
|
||||||
|
|
||||||
|
const htmlContent = templateRender('modules/invoice-regular', {
|
||||||
|
organizationName,
|
||||||
|
organizationEmail,
|
||||||
|
saleInvoice,
|
||||||
|
...i18n
|
||||||
|
});
|
||||||
|
const pdfContent = await this.pdfService.pdfDocument(htmlContent);
|
||||||
|
|
||||||
|
return pdfContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
server/src/services/Sales/SaleInvoiceTransformer.ts
Normal file
60
server/src/services/Sales/SaleInvoiceTransformer.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import { ISaleInvoice } from 'interfaces';
|
||||||
|
import { Transformer } from 'lib/Transformer/Transformer';
|
||||||
|
import { formatNumber } from 'utils';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class SaleInvoiceTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
protected includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formattedInvoiceDate',
|
||||||
|
'formattedDueDate',
|
||||||
|
'formattedAmount',
|
||||||
|
'formattedDueAmount',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice date.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedInvoiceDate = (invoice): string => {
|
||||||
|
return this.formatDate(invoice.invoiceDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted due date.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDueDate = (invoice): string => {
|
||||||
|
return this.formatDate(invoice.dueDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice amount.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (invoice): string => {
|
||||||
|
return formatNumber(invoice.balance, {
|
||||||
|
currencyCode: invoice.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice due amount.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDueAmount(invoice) {
|
||||||
|
return formatNumber(invoice.dueAmount, {
|
||||||
|
currencyCode: invoice.currencyCode,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,20 +22,8 @@ import { ServiceError } from 'exceptions';
|
|||||||
import CustomersService from 'services/Contacts/CustomersService';
|
import CustomersService from 'services/Contacts/CustomersService';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
||||||
|
import { ERRORS } from './constants';
|
||||||
const ERRORS = {
|
import SaleEstimateTransfromer from './Estimates/SaleEstimateTransformer';
|
||||||
SALE_ESTIMATE_NOT_FOUND: 'SALE_ESTIMATE_NOT_FOUND',
|
|
||||||
CUSTOMER_NOT_FOUND: 'CUSTOMER_NOT_FOUND',
|
|
||||||
SALE_ESTIMATE_NUMBER_EXISTANCE: 'SALE_ESTIMATE_NUMBER_EXISTANCE',
|
|
||||||
ITEMS_IDS_NOT_EXISTS: 'ITEMS_IDS_NOT_EXISTS',
|
|
||||||
SALE_ESTIMATE_ALREADY_DELIVERED: 'SALE_ESTIMATE_ALREADY_DELIVERED',
|
|
||||||
SALE_ESTIMATE_CONVERTED_TO_INVOICE: 'SALE_ESTIMATE_CONVERTED_TO_INVOICE',
|
|
||||||
SALE_ESTIMATE_ALREADY_REJECTED: 'SALE_ESTIMATE_ALREADY_REJECTED',
|
|
||||||
SALE_ESTIMATE_ALREADY_APPROVED: 'SALE_ESTIMATE_ALREADY_APPROVED',
|
|
||||||
SALE_ESTIMATE_NOT_DELIVERED: 'SALE_ESTIMATE_NOT_DELIVERED',
|
|
||||||
SALE_ESTIMATE_NO_IS_REQUIRED: 'SALE_ESTIMATE_NO_IS_REQUIRED',
|
|
||||||
CUSTOMER_HAS_SALES_ESTIMATES: 'CUSTOMER_HAS_SALES_ESTIMATES',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sale estimate service.
|
* Sale estimate service.
|
||||||
@@ -64,6 +52,9 @@ export default class SaleEstimateService implements ISalesEstimatesService{
|
|||||||
@Inject()
|
@Inject()
|
||||||
autoIncrementOrdersService: AutoIncrementOrdersService;
|
autoIncrementOrdersService: AutoIncrementOrdersService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
saleEstimateTransformer: SaleEstimateTransfromer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale estimate or throw service error.
|
* Retrieve sale estimate or throw service error.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -404,13 +395,13 @@ export default class SaleEstimateService implements ISalesEstimatesService{
|
|||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
const estimate = await SaleEstimate.query()
|
const estimate = await SaleEstimate.query()
|
||||||
.findById(estimateId)
|
.findById(estimateId)
|
||||||
.withGraphFetched('entries')
|
.withGraphFetched('entries.item')
|
||||||
.withGraphFetched('customer');
|
.withGraphFetched('customer');
|
||||||
|
|
||||||
if (!estimate) {
|
if (!estimate) {
|
||||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_FOUND);
|
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return estimate;
|
return this.saleEstimateTransformer.transform(estimate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -457,7 +448,7 @@ export default class SaleEstimateService implements ISalesEstimatesService{
|
|||||||
.pagination(filter.page - 1, filter.pageSize);
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
salesEstimates: results,
|
salesEstimates: this.saleEstimateTransformer.transform(results),
|
||||||
pagination,
|
pagination,
|
||||||
filterMeta: dynamicFilter.getResponseMeta(),
|
filterMeta: dynamicFilter.getResponseMeta(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import CustomersService from 'services/Contacts/CustomersService';
|
|||||||
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
||||||
import JournalPosterService from './JournalPosterService';
|
import JournalPosterService from './JournalPosterService';
|
||||||
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
||||||
|
import SaleInvoiceTransfromer from './SaleInvoiceTransformer';
|
||||||
import { ERRORS } from './constants';
|
import { ERRORS } from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,6 +75,9 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
autoIncrementOrdersService: AutoIncrementOrdersService;
|
autoIncrementOrdersService: AutoIncrementOrdersService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
saleInvoiceTransformer: SaleInvoiceTransfromer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate whether sale invoice number unqiue on the storage.
|
* Validate whether sale invoice number unqiue on the storage.
|
||||||
*/
|
*/
|
||||||
@@ -639,13 +643,13 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
|
|
||||||
const saleInvoice = await SaleInvoice.query()
|
const saleInvoice = await SaleInvoice.query()
|
||||||
.findById(saleInvoiceId)
|
.findById(saleInvoiceId)
|
||||||
.withGraphFetched('entries')
|
.withGraphFetched('entries.item')
|
||||||
.withGraphFetched('customer');
|
.withGraphFetched('customer');
|
||||||
|
|
||||||
if (!saleInvoice) {
|
if (!saleInvoice) {
|
||||||
throw new ServiceError(ERRORS.SALE_INVOICE_NOT_FOUND);
|
throw new ServiceError(ERRORS.SALE_INVOICE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return saleInvoice;
|
return this.saleInvoiceTransformer.transform(saleInvoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -698,7 +702,7 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
.pagination(filter.page - 1, filter.pageSize);
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
salesInvoices: results,
|
salesInvoices: this.saleInvoiceTransformer.transform(results),
|
||||||
pagination,
|
pagination,
|
||||||
filterMeta: dynamicFilter.getResponseMeta(),
|
filterMeta: dynamicFilter.getResponseMeta(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,16 +29,8 @@ import InventoryService from 'services/Inventory/Inventory';
|
|||||||
import { ACCOUNT_PARENT_TYPE } from 'data/AccountTypes';
|
import { ACCOUNT_PARENT_TYPE } from 'data/AccountTypes';
|
||||||
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
||||||
import CustomersService from 'services/Contacts/CustomersService';
|
import CustomersService from 'services/Contacts/CustomersService';
|
||||||
|
import { ERRORS } from './Receipts/constants';
|
||||||
const ERRORS = {
|
import SaleReceiptTransfromer from './Receipts/SaleReceiptTransformer';
|
||||||
SALE_RECEIPT_NOT_FOUND: 'SALE_RECEIPT_NOT_FOUND',
|
|
||||||
DEPOSIT_ACCOUNT_NOT_FOUND: 'DEPOSIT_ACCOUNT_NOT_FOUND',
|
|
||||||
DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET: 'DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET',
|
|
||||||
SALE_RECEIPT_NUMBER_NOT_UNIQUE: 'SALE_RECEIPT_NUMBER_NOT_UNIQUE',
|
|
||||||
SALE_RECEIPT_IS_ALREADY_CLOSED: 'SALE_RECEIPT_IS_ALREADY_CLOSED',
|
|
||||||
SALE_RECEIPT_NO_IS_REQUIRED: 'SALE_RECEIPT_NO_IS_REQUIRED',
|
|
||||||
CUSTOMER_HAS_SALES_INVOICES: 'CUSTOMER_HAS_SALES_INVOICES',
|
|
||||||
};
|
|
||||||
|
|
||||||
@Service('SalesReceipts')
|
@Service('SalesReceipts')
|
||||||
export default class SalesReceiptService implements ISalesReceiptsService {
|
export default class SalesReceiptService implements ISalesReceiptsService {
|
||||||
@@ -69,6 +61,9 @@ export default class SalesReceiptService implements ISalesReceiptsService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
customersService: CustomersService;
|
customersService: CustomersService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
saleReceiptTransformer: SaleReceiptTransfromer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate whether sale receipt exists on the storage.
|
* Validate whether sale receipt exists on the storage.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
@@ -397,14 +392,14 @@ export default class SalesReceiptService implements ISalesReceiptsService {
|
|||||||
|
|
||||||
const saleReceipt = await SaleReceipt.query()
|
const saleReceipt = await SaleReceipt.query()
|
||||||
.findById(saleReceiptId)
|
.findById(saleReceiptId)
|
||||||
.withGraphFetched('entries')
|
.withGraphFetched('entries.item')
|
||||||
.withGraphFetched('customer')
|
.withGraphFetched('customer')
|
||||||
.withGraphFetched('depositAccount');
|
.withGraphFetched('depositAccount');
|
||||||
|
|
||||||
if (!saleReceipt) {
|
if (!saleReceipt) {
|
||||||
throw new ServiceError(ERRORS.SALE_RECEIPT_NOT_FOUND);
|
throw new ServiceError(ERRORS.SALE_RECEIPT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return saleReceipt;
|
return this.saleReceiptTransformer.transform(saleReceipt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -456,7 +451,7 @@ export default class SalesReceiptService implements ISalesReceiptsService {
|
|||||||
.pagination(filter.page - 1, filter.pageSize);
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
salesReceipts: results,
|
salesReceipts: this.saleReceiptTransformer.transform(results),
|
||||||
pagination,
|
pagination,
|
||||||
filterMeta: dynamicFilter.getResponseMeta(),
|
filterMeta: dynamicFilter.getResponseMeta(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import path from 'path';
|
||||||
import accounting from 'accounting';
|
import accounting from 'accounting';
|
||||||
|
import pug from 'pug';
|
||||||
import Currencies from 'js-money/lib/currency';
|
import Currencies from 'js-money/lib/currency';
|
||||||
import definedOptions from 'data/options';
|
import definedOptions from 'data/options';
|
||||||
|
|
||||||
@@ -378,7 +380,13 @@ const mergeObjectsBykey = (object1, object2, key) => {
|
|||||||
return _.values(merged);
|
return _.values(merged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function templateRender(filePath, options) {
|
||||||
|
const basePath = path.join(__dirname, '../../resources/views');
|
||||||
|
return pug.renderFile(`${basePath}/${filePath}.pug`, options);
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
templateRender,
|
||||||
accumSum,
|
accumSum,
|
||||||
increment,
|
increment,
|
||||||
hashPassword,
|
hashPassword,
|
||||||
|
|||||||
Reference in New Issue
Block a user