[init] init version

This commit is contained in:
2023-10-10 03:58:47 +07:00
commit 73359d5f24
299 changed files with 55909 additions and 0 deletions

16
.editorconfig Normal file
View File

@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

42
.gitignore vendored Normal file
View File

@@ -0,0 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

4
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}

20
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "pwa-chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}

42
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}

161
@note/input.html Normal file
View File

@@ -0,0 +1,161 @@
------------------------------------------------------------
# INPUT
------------------------------------------------------------
<mat-label></mat-label>
<mat-form-field>
<input
matInput
name="KEY"
#KEY="ngModel"
[(ngModel)]="dataForm.KEY"
required
>
<mat-error *ngIf="isFieldValid(ngf, KEY)"> กรุณากรอกข้อมูล</mat-error>
</mat-form-field>
------------------------------------------------------------
# textarea
------------------------------------------------------------
<mat-form-field>
<mat-label></mat-label>
<textarea
matInput
name="KEY"
#KEY="ngModel"
[(ngModel)]="dataForm.KEY"
required
>
</textarea>
<mat-error *ngIf="isFieldValid(ngf, KEY)"> กรุณากรอกข้อมูล</mat-error>
</mat-form-field>
------------------------------------------------------------
# SELECT
------------------------------------------------------------
<mat-form-field>
<mat-label></mat-label>
<mat-select
name="KEY"
#KEY="ngModel"
[(ngModel)]="dataForm.KEY"
required
>
<mat-option [value]=""></mat-option>
<mat-option
*ngFor="let item of select.KEY"
[value]="item.KEY">
{{item.KEY}}
</mat-option>
</mat-select>
<mat-error *ngIf="isFieldValid(ngf, KEY)"> กรุณากรอกข้อมูล</mat-error>
</mat-form-field>
------------------------------------------------------------
# SELECT 2
------------------------------------------------------------
<mat-label></mat-label>
<ng-select
name="budget_year_uid"
#KEY="ngModel"
[(ngModel)]="dataForm.KEY"
(change)="onSelect()"
appendTo="body"
required
>
<ng-option value=""></ng-option>
<ng-option
*ngFor="let item of select.KEY"
[value]="item.KEY">
{{item.KEY}}
</ng-option>
</ng-select>
<mat-error *ngIf="isFieldValid(ngf, KEY)"> กรุณากรอกข้อมูล</mat-error>
<mat-label></mat-label>
<ng-select
#KEY="ngModel"
(change)="onSelectionChange('KEY',$event)"
[(ngModel)]="dataForm.KEY"
[items]="select.KEY"
appendTo="body"
bindLabel="KEY"
bindValue="KEY"
name="KEY"
>
</ng-select>
<mat-error *ngIf="isFieldValid(ngf, KEY)"> กรุณากรอกข้อมูล</mat-error>
------------------------------------------------------------
# AUTOCOMPLETE
------------------------------------------------------------
<mat-form-field>
<mat-label></mat-label>
<input
matInput
name="KEY"
#KEY="ngModel"
[(ngModel)]="dataForm.KEY"
[matAutocomplete]="MATAUTO"
>
<mat-icon matSuffix>search</mat-icon>
</mat-form-field>
<mat-autocomplete #MATAUTO="matAutocomplete" autoActiveFirstOption>
<mat-option
(onSelectionChange)="onSelectionChange('KEY', item)"
*ngFor="let item of select.KEY | searchAuto : dataForm.KEY : 'KEY'"
[value]="item.KEY">
{{item.KEY}}
</mat-option>
</mat-autocomplete>
------------------------------------------------------------
# DATEPICKER
------------------------------------------------------------
<mat-form-field>
<mat-label>วันที่</mat-label>
<input
matInput
name="KEY"
#KEY="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataForm.KEY"
[matDatepicker]="dpkName"
readonly
required
/>
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
<mat-error *ngIf="isFieldValid(ngf, KEY)"> กรุณากรอกข้อมูล</mat-error>
</mat-form-field>
------------------------------------------------------------
# DIALOG
------------------------------------------------------------
<form #ngf="ngForm" (ngSubmit)="onSubmit(ngf)" autocomplete="off" class="dialog-main dialog-form ">
<div class="dialog-main">
<div class="dialog-header">
<h2>{{title}}</h2>
</div>
<div class="dialog-body">
</div>
<div class="dialog-footer">
<button cdkFocusInitial mat-dialog-close mat-raised-button type="button">ยกเลิก</button>
<button class="bg-bpi-primary-color" color="primary" mat-raised-button type="submit">บันทึก</button>
</div>
</div>
</form>
------------------------------------------------------------
# ICON LINK
------------------------------------------------------------
<i [routerLink]="['/app/research-and-evaluation/evaluate-collect/send', item.budget_policy_uid]" class="material-icons" style="cursor: pointer;color: #F8A300;">create</i>

140
@note/note.txt Normal file
View File

@@ -0,0 +1,140 @@
ประเมินราคา/ค่ามัดจำ
ประเมินราคาครั้งที่ 1
ประเมินราคาครั้งที่ 2
ประเมินราคาครั้งที่ 3
การเงิน
ใบเสนอราคา
ใบแจ้งชำระเงิน
ใบแจ้งหนี้
รับชำระเงิน/ใบเสร็จรับเงิน
คืนเงินค่ามัดจำ
สัญญา
ทำสัญญา
ข้อมูลการผ่อนชำระ
คลังสินค้า
สินค้า
ค่าเบี่ยงเบนการวัด
ยี่ห้อ
หมวดหมู่
ตั้งค่าระบบ
ลูกค้า
Appraisals
AppraisalP1
AppraisalP2
AppraisalP3
finance
quotation
bill payment
invoice
Receive payment/receipt
refund deposit
contract
make a contract
installment information
warehouse
product
measurement deviation
brand
category
system settings
customer(แก้ไขแล้ว)เรียกคืนต้นฉบับ
'code','name','brandId','size','color','year','price','latestPrice',
กรอกข้อมูลการจัดผ่อน (ราคาล่าสุด 257,000.00 บาท)
วันที่เริ่มจัดผ่อน
8/03/2023
บาท (THB)
ราคา (Price)
499,999.00
บาท (THB)
มัดจำแม่ค้า
0.00
บาท (THB)
มัดจำ CMFS
150,000.00
บาท (THB)
เงินต้นคงเหลือ (Total)
349,000.00
บาท (THB)
ต้องการผ่อน (Term)
3
งวด
ล้างข้อมูล
คำนวน
รายละเอียดค่าใช้จ่ายในการโอนเงิน
มัดจำ CMFS Deposit
150,000.00
บาท (THB)
หัก เงินมัดจำแม่ค้า
0.00
บาท (THB)
บวก Packing
500.00
บาท (THB)
บวก Luxury handbag
authentication
3,500.00
บาท (THB)
บวก Bank fee,
Insurance , Storage
200.00
บาท (THB)
สรุปยอดโอน
154,200.00
บาท (THB)
งวดที่
กำหนดจ่ายวันที่ Due date
เงินต้น
Principle
ดอกเบี้ย(บาท)
Interest Total
Bank fee, Insurance ,Storage
รวมยอดจ่ายต่อเดือน Total payment
เงินต้นคงเหลือ Principle Total
ข้อมูลลูกค้า
ชื่อลูกค้า
นามสกุล
เบอร์โทร
นางสาวน้ำค้าง
ทดสอบศรี
0896765555
รหัสสินค้า
เลขที่ใบเสนอราคา
A660092
As00022
ชื่อพนักงาน
นางสาวปาดวาด ศิริทรัพย์
customerFirstName
customerLastName
customerPhone
customerIdentificationCard
customerIdentificationCardImage
customerAddress
customerEmail
customerLine
customerLineShop
customerFacebook
customerOccupation: string;
customerIg
<button type="button" class="btn btn-export" (click)="onExport()">Export</button>
onExport() {
const filter = generateParamsValue(this.dataFilter);
const url = `${API.customer}/export?${filter ? '&' + filter : '' }`;
window.open(url);
}

14
DockerFile Normal file
View File

@@ -0,0 +1,14 @@
FROM node:16 AS compile-image
WORKDIR /opt/ng
COPY package.json /opt/ng/package.json
RUN npm install
RUN npm install -g @angular/cli
ENV PATH="./node_modules/.bin:$PATH"
COPY . ./
RUN ng build --configuration production --base-href /cm-finance-web/ --deploy-url /cm-finance-web/
FROM nginx
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=compile-image /opt/ng/dist/cm-finance-web /usr/share/nginx/html

27
README.md Normal file
View File

@@ -0,0 +1,27 @@
# CmFinanceWeb
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.2.2.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

121
angular.json Normal file
View File

@@ -0,0 +1,121 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"cm-finance-web": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/cm-finance-web",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"node_modules/bootstrap-icons/font/bootstrap-icons.scss",
"src/styles/app.scss",
"src/styles.scss"
],
"allowedCommonJsDependencies": [
"sweetalert2",
"autoprefixer"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
]
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "cm-finance-web:build:production"
},
"development": {
"browserTarget": "cm-finance-web:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "cm-finance-web:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": false
}
}

23
bitbucket-pipelines.yml Normal file
View File

@@ -0,0 +1,23 @@
pipelines:
branches:
master:
- step:
size: 2x
services:
- docker
caches:
- docker
script: # Modify the commands below to build your repository.
- docker login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD
- docker build -f DockerFile -t 71dev/cm-finance-web:dev .
- docker push 71dev/cm-finance-web:dev
- step:
name: Deploy to kubernates
image: atlassian/pipelines-kubectl
script:
- echo $KUBE_CONFIG_DELL | base64 -d > kubeconfig
- kubectl --insecure-skip-tls-verify --kubeconfig=kubeconfig rollout restart deployment/cm-finance-web-deployment -n cm-finance-web
definitions:
services:
docker:
memory: 7128

30
nginx.conf Normal file
View File

@@ -0,0 +1,30 @@
server {
gzip on;
gzip_types
text/plain
text/css
text/js
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
image/svg+xml;
listen 8080;
gzip_proxied no-cache no-store private expired auth;
gzip_min_length 1000;
root /usr/share/nginx/html;
include /etc/nginx/mime.types;
location / {
try_files $uri /index.html;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
}
}

22203
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

48
package.json Normal file
View File

@@ -0,0 +1,48 @@
{
"name": "cm-finance-web",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --hmr --configuration development",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^15.2.0",
"@angular/cdk": "^15.2.2",
"@angular/common": "^15.2.0",
"@angular/compiler": "^15.2.0",
"@angular/core": "^15.2.0",
"@angular/forms": "^15.2.0",
"@angular/material": "^15.2.2",
"@angular/platform-browser": "^15.2.0",
"@angular/platform-browser-dynamic": "^15.2.0",
"@angular/router": "^15.2.0",
"@ng-select/ng-select": "^10.0.3",
"bootstrap-icons": "^1.10.3",
"date-fns": "^2.29.3",
"rxjs": "~7.8.0",
"sweetalert2": "^11.7.3",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.2.2",
"@angular/cli": "~15.2.2",
"@angular/compiler-cli": "^15.2.0",
"@tailwindcss/forms": "^0.5.3",
"@types/jasmine": "~4.3.0",
"autoprefixer": "^10.4.14",
"jasmine-core": "~4.5.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.7",
"typescript": "~4.9.4"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -0,0 +1,23 @@
import { Component } from "@angular/core";
import { CDialogConfig, IDialogConfig } from "../interface/Dialog";
@Component({ template: "", standalone: true })
export class BaseFormComponent {
dataForm: any = {};
dataFilter: any = {};
ids: any;
action: any;
params: any = {};
dialogConfig: IDialogConfig = CDialogConfig;
public isFieldValid(form: any, field: any) {
return field.errors && (field.dirty || field.touched || form.submitted);
}
}

View File

@@ -0,0 +1,123 @@
import {Component, ViewChild} from '@angular/core';
import {MatPaginator} from '@angular/material/paginator';
import {MatSort} from '@angular/material/sort';
import generateParamsValue from '../utils/GenerateParamsValue';
import {MatTableDataSource} from '@angular/material/table';
import {IListPageDefault, IListResponse} from "../interface/ListResponse";
import {CDialogConfig, IDialogConfig} from "../interface/Dialog";
import { STORAGE } from "../../@config/app";
@Component({template: ''})
export abstract class BaseListComponent {
dataFilter: any = {};
dataSource: any = [];
dataSourceList = new MatTableDataSource<any>([]);
dataSourceCount = 0;
pageSizeOptions = [10, 20, 50];
pageIndex: number = 1;
pageSize: number = 10;
totalItem: any = 0;
totalOfElement: number = 10;
isTab: string = '';
storage: any = STORAGE;
dialogConfig: IDialogConfig = CDialogConfig;
default: IListPageDefault = {
pageIndex: 1,
pageSize: 10,
};
@ViewChild(MatSort, {static: true}) sort: MatSort | any;
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator | any;
protected setParams(url: string, $event? : any) {
this.pageIndex = $event ? $event.pageIndex + 1 : this.default.pageIndex;
this.pageSize = $event ? $event.pageSize : this.default.pageSize;
const filter = generateParamsValue(this.dataFilter);
return `${url}?page=${this.pageIndex}&perPage=${this.pageSize}${filter ? '&' + filter : '' }`;
}
protected setDataSource<T>(data: IListResponse<T>) {
this.totalItem = data ? data.totalItem : 0;
this.totalOfElement = data ? data.totalOfElement : 0;
this.paginator.length = data ? data.totalItem : 0;
this.dataSourceCount = data ? data.totalItem : 0;
return data ? data.data : [];
}
protected getCurrentPage() {
return {pageIndex: this.pageIndex - 1, pageSize: this.pageSize};
}
protected setDataSourceList<T>(data : any) {
this.dataSourceList = new MatTableDataSource<T>(data);
this.dataSourceList.paginator = this.paginator;
this.dataSourceCount = data ? data.length : 0;
}
protected setFilterPredicate<T>() {
this.dataSourceList.filterPredicate = (data: T, filtersJson: string) => {
const matchFilter : any = [];
const filters = JSON.parse(filtersJson);
filters.forEach((filter : any) => {
// @ts-ignore
const val = data[filter.id] ? data[filter.id] : '';
filter.value = filter.value ? filter.value : '';
matchFilter.push(val.toLowerCase().includes(filter.value.toLowerCase()));
});
return matchFilter.every(Boolean);
};
}
protected onFilterPredicate() {
const tableFilters : any = [];
Object.keys(this.dataFilter).forEach(key => {
if (key) tableFilters.push({id: key, value: this.dataFilter[key]});
});
this.dataSourceList.filter = JSON.stringify(tableFilters);
if (this.dataSourceList.paginator) {
this.dataSourceList.paginator.firstPage();
}
}
protected setFilterLocal(page : any) {
const data = {
page,
filter: this.dataFilter
};
const filterLocalData = [];
const filterLocal: any = localStorage.getItem('filterLocal');
if (filterLocal) {
const parse = JSON.parse(filterLocal);
const mapData = parse.map((item : any) => {
if (item.page === page) return data;
return item;
});
return localStorage.setItem('filterLocal', JSON.stringify(mapData));
}
filterLocalData.push(data);
localStorage.setItem('filterLocal', JSON.stringify(filterLocalData));
}
protected getFilterLocal(page : any) {
const filterLocal: any = localStorage.getItem('filterLocal');
if (!filterLocal) return;
const parse = JSON.parse(filterLocal);
const isPage = parse.filter((f : any) => f.page === page);
if (!isPage[0]) return;
this.dataFilter = isPage[0].filter;
}
}

View File

@@ -0,0 +1,15 @@
import {Component} from '@angular/core';
import {CDialogConfig, IDialogConfig} from "../interface/Dialog";
@Component({template: ''})
export abstract class BasePopupComponent {
ids: any;
dataForm: any = {};
dialogConfig: IDialogConfig = CDialogConfig;
public isFieldValid(form : any, field : any) {
return field.errors && (field.dirty || field.touched || form.submitted);
}
}

View File

@@ -0,0 +1,21 @@
import {EAction} from "../../@config/app";
export const CDialogConfig: IDialogConfig = {
width: '80%',
height: '80%',
data: {}
};
export interface IDialogConfig {
width: string;
height: string;
data: IDialogConfigData;
}
export interface IDialogConfigData {
action?: EAction;
ids?: string;
datas?: any;
dataForm?: any;
}

View File

@@ -0,0 +1,12 @@
export interface IListResponse<T> {
data: T[];
totalItem: number;
totalOfElement: number;
page: number;
rowsPerPage: number;
lastPage: number;
}
export interface IListPageDefault {
pageIndex: number;
pageSize: number;
}

View File

@@ -0,0 +1,201 @@
export interface IQuotation {
id?: number
step?: number
quotationNo?: string
productId?: any
typeCode?: any
productNo?: string
productName?: string
productBrandName?: string
productSize?: string
productColor?: string
productWeight?: any
productYear?: string
productPrice?: string
productLatestPrice?: string
productImages?: any
images?: any
openingVideo?: any
coverImage?: string
startDate?: string
deposit?: string
cmfsDeposit?: string
price?: string
lessSellerDeposit?: string
sellerDeposit?: string
sellerDeposit2ndTime?: any
sellerDeposit3rdTime?: any
sellerDepositSum?: any
plusPacking?: string
plusLuxuryHandbag?: string
principalBalanceTotal?: string
wantToInstallmentTerm?: number
installment?: any
plusBankFee?: string
transferSummary?: string
customerId?: number
sellerId?: any
customerPrefix?: any
customerFirstName?: string
customerLastName?: string
customerPhone?: string
customerIdCard?: any
customerIdCardImage?: any
customerAddress?: any
customerEmail?: any
customerLine?: any
customerLineShop?: any
customerFacebook?: any
customerOccupation?: any
customerIg?: any
userFullName?: string
sellerName?: string
sellerPhone?: string
sellerEmail?: any
sellerLine?: any
sellerLineShop?: any
sellerFacebook?: any
sellerIg?: any
sellerSnProduct?: any
status?: string
status2ndTime?: string
status3rdTime?: string
statusContract?: string
contractDate?: any
contractApprovedDate?: any
contractCancelDate?: any
contractBankName?: any
contractAccountName?: any
contractAccountNumber?: any
contractDetail?: any
isContractClosing?: any
contractPrice?: any
contractInterest?: any
contractPriceSum?: any
receivedDate?: any
statusWarehouse?: any
areaId?: any
areaCode?: any
areaName?: any
areaRoomName?: any
areaLocation?: any
areaFloor?: any
areaRoomSize?: any
storageBoxId?: any
storageBoxCode?: any
storageBoxName?: any
storageBoxSize?: any
disbursementDate?: any
disbursementType?: any
deliveryWorker?: any
isCheckInspection?: any
isReceived?: any
type?: string
pickType?: any
settingInterestRate?: any
settingCmfsDeposit?: any
priceDisbursement?: any
sellerPaymentAccountName?: string
sellerPaymentPayee?: string
sellerPaymentMethod?: string
sellerPaymentDate?: string
sellerPaymentAmount?: string
sellerPaymentAccountNumber?: string
isInvoice?: string
isPaying?: string
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: number
productMeasurement?: IQuotationProductMeasurement[]
quotationDetail?: IQuotationDetail[]
customer?: IQuotationCustomer
seller?: any
discount?: any
}
export interface IQuotationCustomer {
id?: number
code?: any
prefix?: string
gender?: string
firstName?: string
lastName?: string
phone?: string
idCard?: string
idCardImage?: string
address?: string
isAddress?: boolean
deliveryAddress?: string
email?: string
line?: string
lineShop?: any
facebook?: string
occupation?: string
ig?: string
status?: any
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: any
}
export interface IQuotationProductMeasurement {
id?: number
productId?: any
masterProductMeasurementId?: any
quotationId?: number
code?: any
name?: string
size?: string
unitId?: number
status?: any
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: any
}
export interface IQuotationDetail {
id?: number
quotationId?: number
installment?: number
dueDate?: string
principle?: string
interestTotal?: string
fee?: string
totalPayment?: string
principleTotal?: string
status?: string
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: any
}
export interface IQuotationPayment {
id?: number
quotationId?: number
quotationNo?: string
productNo?: string
productName?: string
customerPrefix?: string
customerFirstName?: string
customerLastName?: string
customerPhone?: string
price?: string
sellerDeposit?: string
deposit?: string
paymentDate?: string
paymentType?: string
paymentAmount?: string
paymentAmountAll?: string
paymentMethod?: string
status?: string
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: any
quotation?: IQuotation
}

View File

@@ -0,0 +1,199 @@
import { OnInit, Directive, HostListener, ElementRef, forwardRef } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { DecimalPipe } from "@angular/common";
export const CURRENCY_INPUT_MASK_DIRECTIVE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CurrencyInputMaskDirective),
multi: true
};
@Directive({
selector: "[appCurrencyInputMask]",
providers: [
CURRENCY_INPUT_MASK_DIRECTIVE_VALUE_ACCESSOR,
DecimalPipe
]
})
export class CurrencyInputMaskDirective implements ControlValueAccessor, OnInit {
private el: HTMLInputElement | any;
private onModelChange: Function | any;
private onModelTouched: Function | any;
private lastNumVal: number | any;
private DECIMAL_MARK = ".";
constructor(
private elementRef: ElementRef,
private decimalPipe: DecimalPipe
) {
}
ngOnInit() {
this.el = this.elementRef.nativeElement;
}
@HostListener("focus", ["$event"])
handleFocus(event: any) {
const strVal: string = this.getInputValue();
const unmaskedStr: string = this.getUnmaskedValue(strVal);
this.updateInputValue(unmaskedStr);
}
@HostListener("cut", ["$event"])
handleCut(event: any) {
setTimeout(() => {
this.inputUpdated();
}, 0);
}
@HostListener("keypress", ["$event"])
handleKeypress(event: any) {
// Restrict characters
const newChar: string = String.fromCharCode(event.which);
const allowedChars: RegExp = /^[\d.]+$/;
if (!allowedChars.test(newChar)) {
event.preventDefault();
return;
}
// Handle decimal mark input
const currentValue: string = event.target.value;
const separatorIdx: number = currentValue.indexOf(this.DECIMAL_MARK);
const hasFractionalPart: boolean = (separatorIdx >= 0);
if (!hasFractionalPart || newChar !== this.DECIMAL_MARK) {
return;
}
const isOutsideSelection = !this.isIdxBetweenSelection(separatorIdx);
if (isOutsideSelection) {
const positionAfterMark = separatorIdx + 1;
this.setCursorPosition(positionAfterMark);
event.preventDefault();
return;
}
}
@HostListener("input", ["$event"])
handleInput(event: any) {
this.inputUpdated();
}
@HostListener("paste", ["$event"])
handlePaste(event: any) {
setTimeout(() => {
this.inputUpdated();
}, 1);
}
@HostListener("blur", ["$event"])
handleBlur(event: any) {
const strVal: string = this.getInputValue();
const numVal: any = this.convertStrToDecimal(strVal);
this.maskInput(numVal);
this.onModelTouched.apply(event);
}
registerOnChange(callbackFunction: Function): void {
this.onModelChange = callbackFunction;
}
registerOnTouched(callbackFunction: Function): void {
this.onModelTouched = callbackFunction;
}
setDisabledState(value: boolean): void {
this.el.disabled = value;
}
writeValue(numValue: number): void {
this.maskInput(numValue);
}
private maskInput(numVal: number): void {
if (!this.isNumeric(numVal)) {
this.updateInputValue("");
return;
}
const strVal: string = this.convertDecimalToStr(numVal);
const newVal: any = this.transformWithPipe(strVal);
this.updateInputValue(newVal);
}
private inputUpdated() {
this.restrictDecimalValue();
const strVal: string = this.getInputValue();
const unmaskedVal: string = this.getUnmaskedValue(strVal);
const numVal: any = this.convertStrToDecimal(unmaskedVal);
if (numVal !== this.lastNumVal) {
this.lastNumVal = numVal;
this.onModelChange(numVal);
}
}
private restrictDecimalValue(): void {
const strVal: string = this.getInputValue();
const dotIdx: number = strVal.indexOf(this.DECIMAL_MARK);
const hasFractionalPart: boolean = (dotIdx >= 0);
if (hasFractionalPart) {
const fractionalPart: string = strVal.substring(dotIdx + 1);
if (fractionalPart.length > 2) {
const choppedVal: string = strVal.substring(0, dotIdx + 3);
this.updateInputValue(choppedVal, true);
return;
}
}
}
private transformWithPipe(str: any) {
return this.decimalPipe.transform(str, "1.2-2");
}
private getUnmaskedValue(value: string): string {
return value.replace(/[^-\d\\.]/g, "");
}
private updateInputValue(value: string, savePosition = false) {
if (savePosition) {
this.saveCursorPosition();
}
this.el.value = value;
}
private getInputValue(): string {
return this.el.value;
}
private convertStrToDecimal(str: any) {
return (this.isNumeric(str)) ? parseFloat(str) : null;
}
private convertDecimalToStr(n: number): string {
return (this.isNumeric(n)) ? n + "" : "";
}
private isNumeric(n: any): boolean {
return !isNaN(parseFloat(n)) && isFinite(n);
}
private saveCursorPosition() {
const position: number = this.el.selectionStart;
setTimeout(() => {
this.setCursorPosition(position);
}, 1);
}
private setCursorPosition(position: number) {
this.el.selectionStart = position;
this.el.selectionEnd = position;
}
private isIdxBetweenSelection(idx: number) {
if (this.el.selectionStart === this.el.selectionEnd) {
return false;
}
return (idx >= this.el.selectionStart && idx < this.el.selectionEnd);
}
}

View File

@@ -0,0 +1,4 @@
export default function deepCopy(data: any) {
if (!data) return {};
return JSON.parse(JSON.stringify(data));
}

View File

@@ -0,0 +1,16 @@
export default function isEmpty(value: any) {
switch (value) {
case '':
case 0:
case '0':
case null:
case false:
case 'null':
case 'undefined':
case undefined:
case typeof value === 'undefined':
return true;
default:
return false;
}
}

View File

@@ -0,0 +1,134 @@
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'filterBy',
pure: false
})
export class FilterPipe implements PipeTransform {
static isFoundOnWalking(value, key) {
let walker = value;
let found = false;
do {
if (
walker.hasOwnProperty(key) ||
Object.getOwnPropertyDescriptor(walker, key)
) {
found = true;
break;
}
// tslint:disable-next-line:no-conditional-assignment
} while ((walker = Object.getPrototypeOf(walker)));
return found;
}
static isNumber(value) {
return !isNaN(parseInt(value, 10)) && isFinite(value);
}
/**
* Checks function's value if type is function otherwise same value
*/
static getValue(value: any): any {
return typeof value === 'function' ? value() : value;
}
private filterByString(filter) {
if (filter) {
filter = filter.toLowerCase();
}
return value =>
!filter ||
(value ? ('' + value).toLowerCase().indexOf(filter) !== -1 : false);
}
private filterByBoolean(filter) {
return value => Boolean(value) === filter;
}
private filterByObject(filter) {
return value => {
for (const key in filter) {
if (key === '$or') {
if (!this.filterByOr(filter.$or)(FilterPipe.getValue(value))) {
return false;
}
continue;
}
if (!value || !FilterPipe.isFoundOnWalking(value, key)) {
return false;
}
if (!this.isMatching(filter[key], FilterPipe.getValue(value[key]))) {
return false;
}
}
return true;
};
}
private isMatching(filter, val) {
switch (typeof filter) {
case 'boolean':
return this.filterByBoolean(filter)(val);
case 'string':
return this.filterByString(filter)(val);
case 'object':
return this.filterByObject(filter)(val);
}
return this.filterDefault(filter)(val);
}
/**
* Filter value by $or
*/
private filterByOr(filter: any[]): (value: any) => boolean {
return (value: any) => {
const length = filter.length;
const arrayComparison = i => value.indexOf(filter[i]) !== -1;
const otherComparison = i => this.isMatching(filter[i], value);
const comparison = Array.isArray(value)
? arrayComparison
: otherComparison;
for (let i = 0; i < length; i++) {
if (comparison(i)) {
return true;
}
}
return false;
};
}
/**
* Default filterDefault function
*/
private filterDefault(filter: any): (value: any) => boolean {
return (value: any) => filter === undefined || filter === value;
}
transform(array: any[], filter: any): any {
if (!array) {
return array;
}
switch (typeof filter) {
case 'boolean':
return array.filter(this.filterByBoolean(filter));
case 'string':
if (FilterPipe.isNumber(filter)) {
return array.filter(this.filterDefault(filter));
}
return array.filter(this.filterByString(filter));
case 'object':
return array.filter(this.filterByObject(filter));
case 'function':
return array.filter(filter);
}
return array.filter(this.filterDefault(filter));
}
}

View File

@@ -0,0 +1,10 @@
export default function generateParamsValue(jsonValue: any) {
let params = '';
Object.keys(jsonValue).forEach((key) => {
if (jsonValue[key]) {
params += key + '=' + jsonValue[key] + '&';
}
});
params = params.substring(0, params.length - 1);
return params;
}

View File

@@ -0,0 +1,41 @@
import {Directive, ElementRef, EventEmitter, HostListener, Output} from '@angular/core';
@Directive({
selector: '[appNumberOnly]'
})
export class NumberOnlyDirective {
// Emit the parsed number value
@Output() numberValue: EventEmitter<number> = new EventEmitter<number>();
// Allow key codes for special events. Reflect :
// Allow decimal numbers with 2 decimal places and negative values
private regex: RegExp = new RegExp(/^\d*\.?\d{0,2}$/g);
// Backspace, tab, end, home
private specialKeys: Array<string> = ['Backspace', 'Tab', 'End', 'Home', '-', 'ArrowLeft', 'ArrowRight', 'Del', 'Delete'];
constructor(private el: ElementRef) {
}
@HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.specialKeys.indexOf(event.key) !== -1) {
return;
}
const current: string = this.el.nativeElement.value;
const position = this.el.nativeElement.selectionStart;
const next: string = [current.slice(0, position), event.key === 'Decimal' ? '.' : event.key, current.slice(position)].join('');
if (next && !String(next).match(this.regex)) {
event.preventDefault();
}
}
@HostListener('input', ['$event'])
onInput(event: KeyboardEvent) {
// Get the parsed number value
const parsedValue = Number(this.el.nativeElement.value);
// Emit the parsed value only if it's a number
if (!isNaN(parsedValue)) {
this.numberValue.emit(parsedValue);
}
}
}

View File

@@ -0,0 +1,22 @@
export function orderByArray(data: any, key: string) {
if (!data) return;
if (data.length === 0) return;
return data.sort((a: any, b: any) => a[key].localeCompare(b[key], 'en', {numeric: true}));
}
export function sortByProperty<T>(array: T[], propName: keyof T, order: 'ASC' | 'DESC') {
array.sort((a, b) => {
if (a[propName] < b[propName]) {
return -1;
}
if (a[propName] > b[propName]) {
return 1;
}
return 0;
});
if (order === 'DESC') {
array.reverse();
}
}

View File

@@ -0,0 +1,22 @@
export function generateUUID() {
// Public Domain/MIT
let d = new Date().getTime(); // Timestamp
let d2 =
(typeof performance !== 'undefined' &&
performance.now &&
performance.now() * 1000) ||
0; // Time in microseconds since page-load or 0 if unsupported
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = Math.random() * 16; // random number between 0 and 16
if (d > 0) {
// Use timestamp until depleted
r = (d + r) % 16 | 0;
d = Math.floor(d / 16);
} else {
// Use microseconds since page-load if supported
r = (d2 + r) % 16 | 0;
d2 = Math.floor(d2 / 16);
}
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
});
}

104
src/app/@config/app.ts Normal file
View File

@@ -0,0 +1,104 @@
import {environment} from "../../environments/environment";
const ENV = {
url : environment.API_URL,
reportUrl : environment.API_REPORT_URL,
}
export const API = {
url : `${ENV.url}` ,
login : `${ENV.url}/auth/login` ,
users : `${ENV.url}/users` ,
userGroup : `${ENV.url}/userGroup` ,
customer : `${ENV.url}/customer` ,
seller : `${ENV.url}/seller` ,
quotation : `${ENV.url}/quotation` ,
quotationDetail : `${ENV.url}/quotationDetail` ,
quotationPayment : `${ENV.url}/quotationPayment` ,
products : `${ENV.url}/products` ,
masterProductBrand : `${ENV.url}/masterProductBrand` ,
masterProductCategory : `${ENV.url}/masterProductCategory` ,
masterProductUnit : `${ENV.url}/masterProductUnit` ,
masterProductMeasurement : `${ENV.url}/masterProductMeasurement` ,
masterArea : `${ENV.url}/masterArea` ,
masterStorageBox : `${ENV.url}/masterStorageBox` ,
attachments : `${ENV.url}/attachments` ,
settings : `${ENV.url}/settings` ,
packet : `${ENV.url}/packet` ,
promotion : `${ENV.url}/promotion` ,
quotationReport : `${ENV.reportUrl}/reports/quotation_report` ,
receiptReport : `${ENV.reportUrl}/reports/receipt_report` ,
installmentContractReport : `${ENV.reportUrl}/reports/installment_contract_report` ,
receiveInventory : `${ENV.reportUrl}/reports/receive_inventory` ,
receiveSendInventory : `${ENV.reportUrl}/reports/receive_send_inventory` ,
receivePickUp : `${ENV.reportUrl}/reports/pick_up` ,
reportOutstandingAccountsReceivable : `${ENV.reportUrl}/reports/outstanding_accounts_receivable` ,
paymentReport : `${ENV.reportUrl}/reports/payment_report` ,
paymentVoucher : `${ENV.reportUrl}/reports/payment_voucher` ,
}
export const STORAGE = {
products : `${ENV.url}/storage/products` ,
images : `${ENV.url}/storage/images` ,
}
export const GENDER = ['ชาย', 'หญิง', 'อื่นๆ'];
export const PREFIX = ['นาย', 'นาง', 'นางสาว', 'อื่นๆ'];
export const TYPE_CODE = ['A', 'C', 'H', 'W'];
export const CONDITIONS = ['new', 'like new', 'used'];
export const SOURCES = ['แม่ค้าแนะนำ', 'ลูกค้าเก่า', 'ลูกค้าใหม่', 'แม่ค้าซื้อ'];
export enum EAction {
CREATE = 'create',
UPDATE = 'update',
DELETE = 'delete',
GET = 'get',
POPUP = 'popup',
SEND = 'send',
ERROR = 'error',
INFO = 'info',
SUCCESS = 'success',
BACK = 'back',
COPY = 'copy',
RELOAD = 'reload',
REFINANCE = 'refinance',
}
export type TAction = `${EAction}`;
export enum EText {
CREATE = 'เพิ่มข้อมูลสำเร็จ',
UPDATE = 'บันทึกสำเร็จ',
DELETE = 'ลบข้อมูลสำเร็จ',
NO_DATA = 'ไม่พบข้อมูล',
ERROR = 'เกิดข้อผิดพลาด',
COPY = 'เพิ่มข้อมูลสำเร็จ',
MAIL = 'ส่งเมลสำเร็จ',
}
export enum EStatusQuotation {
PENDING = 'pending',
PAID = 'paid',
WAIT = 'wait',
DUE = 'due',
EVALUATED = 'evaluated',
COMPLETE = 'complete',
}
export enum EStatusContract {
WAIT = 'wait',
PENDING = 'pending',
APPROVED = 'approved',
CANCEL = 'cancel',
}
export enum EStatusWarehouse {
WAREHOUSE = 'warehouse',
DISBURSEMENT = 'disbursement',
}

205
src/app/@config/menus.ts Normal file
View File

@@ -0,0 +1,205 @@
export interface MENU {
link?: string;
type: | 'link' | 'heading' | 'collapsable';
icon?: string;
name?: string;
params?: any[];
badge?: string;
roles?: any[];
children?: any[];
permission?: string;
collapsed?: boolean;
notShowing?: boolean;
isChecked?: boolean;
}
export const MENU: MENU[] = [
// {
// name: 'ประเมินราคา/ค่ามัดจำ',
// link: 'appraisal',
// permission: 'appraisal',
// icon: 'bi bi-ui-checks',
// params: [],
// badge: '',
// type: 'collapsable',
// children: [
// {
// name: 'ประเมินราคาครั้งที่ 1',
// link: 'appraisal/1st-time',
// permission: 'appraisal-1st-time',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// children: [
// {
// name: 'ประวัติการสร้างใบเสนอราคา',
// link: 'appraisal/1st-time/history',
// permission: 'appraisal-1st-time-history',
// notShowing: true,
// children: [
// {
// name: 'ใบเสนอราคา',
// link: 'appraisal/1st-time/history/pdf',
// permission: 'appraisal-1st-time-pdf',
// notShowing: true,
// }
// ]
// },
// ]
// },
// {
// name: 'ประเมินราคาครั้งที่ 2',
// link: 'appraisal/2nd-time',
// permission: 'appraisal-2nd-time',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// },
// {
// name: 'ประเมินราคาครั้งที่ 3',
// link: 'appraisal/3rd-time',
// permission: 'appraisal-3rd-time',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// },
// ]
// },
// {
// name: 'การเงิน',
// link: 'finance',
// permission: 'finance',
// icon: 'bi bi-coin',
// params: [],
// badge: '',
// type: 'collapsable',
// children: [
// {
// name: 'รับชำระเงิน/ใบเสร็จรับเงิน',
// link: 'finance/payment',
// permission: 'payment',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// children: [
// {
// name: 'ใบเสร็จรับเงิน',
// link: 'finance/payment/paid/pdf',
// permission: 'finance-payment-pdf',
// notShowing: true,
// }
// ]
// },
// {
// name: 'การแจ้งหนี้/ตั้งเจ้าหนี้',
// link: 'finance/invoice',
// permission: 'invoice',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// },
// {
// name: 'การจ่ายชำระเงิน',
// link: 'finance/paying',
// permission: 'paying',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// },
// ]
// },
// {
// name: 'สัญญา',
// link: 'contract',
// permission: 'contract',
// icon: 'bi bi-file-earmark-text-fill',
// params: [],
// badge: '',
// type: 'collapsable',
// children: [
// {
// name: 'ทำสัญญา',
// link: 'contract/make',
// permission: 'contract-make',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// },
// {
// name: 'อนุมัติสัญญา',
// link: 'contract/approved',
// permission: 'contract-approved',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// }
// ]
// },
{
name: 'Manage',
link: 'manage',
permission: 'manage',
icon: 'bi bi-card-checklist',
params: [],
badge: '',
type: 'collapsable',
children: [
{
name: 'Manage KYC',
link: 'manage/kyc',
permission: 'manage-kyc',
type: 'link',
icon: '',
params: [],
badge: '',
// children: [
// {
// name: 'ประวัติการสร้างใบเสนอราคา',
// link: 'appraisal/1st-time/history',
// permission: 'appraisal-1st-time-history',
// notShowing: true,
// children: [
// {
// name: 'ใบเสนอราคา',
// link: 'appraisal/1st-time/history/pdf',
// permission: 'appraisal-1st-time-pdf',
// notShowing: true,
// }
// ]
// },
// ]
},
// {
// name: 'ประเมินราคาครั้งที่ 2',
// link: 'appraisal/2nd-time',
// permission: 'appraisal-2nd-time',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// },
// {
// name: 'ประเมินราคาครั้งที่ 3',
// link: 'appraisal/3rd-time',
// permission: 'appraisal-3rd-time',
// type: 'link',
// icon: '',
// params: [],
// badge: '',
// },
]
},
];

View File

@@ -0,0 +1,185 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PagesLayoutsComponent } from "./pages/@layouts/layouts.component";
import { ComingsoonComponent } from "./pages/@comingsoon/comingsoon.component";
import { AppGuard } from "./app.guard";
import { PageBlankModule } from "./pages/page-blank/page-blank.module";
const routes: Routes = [
{
path: '',
redirectTo: 'pages',
pathMatch: 'full'
},
{
path: 'auth',
loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule)
},
{
path: 'pages',
component: PagesLayoutsComponent,
canActivate: [AppGuard],
canActivateChild: [AppGuard],
children: [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{
path: 'dashboard',
loadChildren: () => import('./pages/dashboard/dashboard.module').then(m => m.DashboardModule)
},
{
path: 'users',
loadChildren: () => import('./pages/users/users.module').then(m => m.UsersModule)
},
{
path: 'manage',
children: [
{
path: 'kyc',
loadChildren: () => import('./pages/appraisal/1st-time/appraisal-1st-time.module').then(m => m.Appraisal1stTimeModule)
},
// {
// path: '2nd-time',
// loadChildren: () => import('./pages/appraisal/2nd-time/appraisal-2nd-time.module').then(m => m.Appraisal2ndTimeModule)
// },
// {
// path: '3rd-time',
// loadChildren: () => import('./pages/appraisal/3rd-time/appraisal-3rd-time.module').then(m => m.Appraisal3rdTimeModule)
// },
]
},
{
path: 'finance',
children: [
{
path: 'payment',
loadChildren: () => import('./pages/finance/payment/finance-payment.module').then(m => m.FinancePaymentModule)
},
{
path: 'refund',
loadChildren: () => import('./pages/page-blank/page-blank.module').then(m => m.PageBlankModule)
},
{
path: 'invoice',
loadChildren: () => import('./pages/finance/invoice/finance-invoice.module').then(m => m.FinanceInvoiceModule)
},
{
path: 'paying',
loadChildren: () => import('./pages/finance/paying/finance-paying.module').then(m => m.FinancePayingModule)
},
]
},
{
path: 'contract',
children: [
{
path: 'make',
loadChildren: () => import('./pages/contract/make/contract-make.module').then(m => m.ContractMakeModule)
},
{
path: 'approved',
loadChildren: () => import('./pages/contract/approved/contract-approved.module').then(m => m.ContractApprovedModule)
}
]
},
// {
// path: 'warehouse',
// children: [
// {
// path: 'received',
// loadChildren: () => import('./pages/warehouse/received/warehouse-received.module').then(m => m.WarehouseReceivedModule)
// },
// {
// path: 'inspection',
// loadChildren: () => import('./pages/warehouse/inspection/warehouse-inspection.module').then(m => m.WarehouseInspectionModule)
// },
// {
// path: 'disbursement',
// loadChildren: () => import('./pages/warehouse/disbursement/warehouse-disbursement.module').then(m => m.WarehouseDisbursementModule)
// },
// {
// path: 'area',
// loadChildren: () => import('./pages/setting/master-area/master-area.module').then(m => m.MasterAreaModule)
// },
// {
// path: 'storage-box',
// loadChildren: () => import('./pages/setting/master-storage-box/master-storage-box.module').then(m => m.MasterStorageBoxModule)
// }
// ]
// },
// {
// path: 'setting',
// children: [
// {
// path: 'products',
// loadChildren: () => import('./pages/setting/products/products.module').then(m => m.ProductsModule)
// },
// {
// path: 'deviation',
// loadChildren: () => import('./pages/setting/setting-deviation/setting-deviation.module').then(m => m.SettingDeviationModule)
// },
// {
// path: 'product-brand',
// loadChildren: () => import('./pages/setting/master-product-brand/master-product-brand.module').then(m => m.MasterProductBrandModule)
// },
// {
// path: 'product-category',
// loadChildren: () => import('./pages/setting/master-product-category/master-product-category.module').then(m => m.MasterProductCategoryModule)
// },
// {
// path: 'product-unit',
// loadChildren: () => import('./pages/setting/master-product-unit/master-product-unit.module').then(m => m.MasterProductUnitModule)
// },
// {
// path: 'product-measurement',
// loadChildren: () => import('./pages/setting/master-product-measurement/master-product-measurement.module').then(m => m.MasterProductMeasurementModule)
// },
// {
// path: 'installment',
// loadChildren: () => import('./pages/setting/setting-installment/setting-installment.module').then(m => m.SettingInstallmentModule)
// },
// {
// path: 'packet',
// loadChildren: () => import('./pages/setting/packet/packet.module').then(m => m.PacketModule)
// },
// {
// path: 'promotion',
// loadChildren: () => import('./pages/setting/promotion/promotion.module').then(m => m.PromotionModule)
// },
// {
// path: 'customer',
// loadChildren: () => import('./pages/setting/customer/customer.module').then(m => m.CustomerModule)
// },
// {
// path: 'seller',
// loadChildren: () => import('./pages/setting/seller/seller.module').then(m => m.SellerModule)
// },
// ]
// },
// {
// path: 'report',
// loadChildren: () => import('./pages/report/report.module').then(m => m.ReportModule)
// },
{
path: 'not-found',
loadChildren: () => import('./pages/errors/errors.module').then(m => m.ErrorsModule)
},
]
},
{ path: '**', redirectTo: 'pages/not-found' }
];
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule]
})
export class AppRoutingModule {
}
export const AppRoutingComponents = [
ComingsoonComponent,
PagesLayoutsComponent
];

View File

@@ -0,0 +1 @@
<router-outlet></router-outlet>

8
src/app/app.component.ts Normal file
View File

@@ -0,0 +1,8 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: []
})
export class AppComponent {}

29
src/app/app.guard.ts Normal file
View File

@@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router';
import { AppService } from './app.service';
@Injectable()
export class AppGuard implements CanActivate, CanActivateChild {
constructor(private router: Router, private app: AppService) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.isLogin();
}
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.isLogin();
}
isLogin() {
return true;
// const user = this.app.auth();
// if (!user) {
// this.router.navigate(['/auth/login']);
// return false;
// }
// return true;
}
}

91
src/app/app.interface.ts Normal file
View File

@@ -0,0 +1,91 @@
export interface IProduct {
id?: number
code?: any
name?: string
desc?: string
brandId?: number
size?: string
weight?: string
color?: string
year?: string
price?: string
latestPrice?: string
coverImage?: any
images?: string
status?: any
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: any
category?: any
condition?: any
model?: any
material?: any
index?: any
productCategory?: IProductCategory[]
productMeasurement?: IProductMeasurement[]
masterProductBrand?: IMasterProductBrand
}
export interface IProductCategory {
productId?: number
categoryId?: number
masterProductCategory?: IMasterProductCategory
}
export interface IMasterProductCategory {
id?: number
code?: string
name?: string
status?: boolean
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: any
}
export interface IProductMeasurement {
id: number
productId: number
code: any
name: string
size: string
unitId: number
status: any
createdDate: string
createdBy: any
updatedDate: string
updatedBy: any
}
export interface IMasterProductBrand {
id?: number
code?: string
name?: string
status?: boolean
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: any
}
export interface IAttachments {
ref?: string
fileOriginalName?: string
encoding?: string
mimetype?: string
fileName?: string
filePath?: string
fileSize?: number
createdDate?: string
createdBy?: any
updatedDate?: string
updatedBy?: any
deletedDate?: any
deletedBy?: any
id?: number
}

41
src/app/app.module.ts Normal file
View File

@@ -0,0 +1,41 @@
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppRoutingComponents, AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {HTTP_INTERCEPTORS, HttpClientModule} from "@angular/common/http";
import {AppService} from "./app.service";
import {AppGuard} from "./app.guard";
import {AppRequestInterceptor} from "./app.request.interceptor";
import {AppSharedModule} from "./app.shared";
@NgModule({
declarations: [
AppComponent,
...AppRoutingComponents
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
AppSharedModule
],
providers: [
AppService,
AppGuard,
{
provide: HTTP_INTERCEPTORS,
useClass: AppRequestInterceptor,
multi: true
},
],
bootstrap: [AppComponent]
})
export class AppModule {
}

View File

@@ -0,0 +1,31 @@
import {Injectable} from '@angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Router} from '@angular/router';
import {AppService} from './app.service';
import {catchError, Observable, throwError} from 'rxjs';
@Injectable()
export class AppRequestInterceptor implements HttpInterceptor {
constructor(private router: Router,private appService: AppService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.appService.token();
if (token) {
request = request.clone({
setHeaders: {Authorization: `Bearer ${token}`}
});
}
return next.handle(request).pipe(
catchError((err) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
this.router.navigate(['/auth']);
}
}
return throwError(err);
})
)
}
}

122
src/app/app.service.ts Normal file
View File

@@ -0,0 +1,122 @@
import {Inject, Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {DOCUMENT, Location} from '@angular/common';
import {from, Observable} from 'rxjs';
import Swal, {SweetAlertResult} from 'sweetalert2'
import {EAction} from "./@config/app";
@Injectable()
export class AppService {
constructor(
@Inject(DOCUMENT) private document: Document,
@Inject(Location) private location: Location,
protected httpClient: HttpClient,
) {
}
setToken(data: any): void {
localStorage.setItem('token', (data));
}
setAuth(data: any): void {
localStorage.setItem('user', JSON.stringify(data));
}
token() {
const token = localStorage.getItem('token');
if (!token) return null;
return token;
}
auth() {
const user = localStorage.getItem('user');
if (!user) {
return null;
}
return JSON.parse(user);
}
async logout() {
localStorage.removeItem('token');
localStorage.removeItem('user');
localStorage.clear();
// await lastValueFrom( this.get(this.LOGOUT_API) )
}
get(url: string): Observable<any> {
return this.httpClient.get<any>(url);
}
post(url: string, value: any, options? : any): Observable<any> {
return this.httpClient.post<any>(url, value, options);
}
delete(url: string, id: string): Observable<any> {
return this.httpClient.delete<any>(`${url}/${id}`);
}
message(action: any = 'info', msg: string = 'กรุณาตรวจสอบข้อมูล') {
Swal.fire({icon: action, text: msg, heightAuto: false});
}
html(action: any = 'info', msg: string = 'กรุณาตรวจสอบข้อมูล') {
Swal.fire({icon: action, html: msg, heightAuto: false});
}
confirm(action: string = '', confirmButtonText: string = 'ตกลง', cancelButtonText: string = 'ยกเลิก'): Observable<SweetAlertResult<boolean>> {
let msg = '';
if (action === EAction.CREATE) msg = 'ต้องการบันทึกข้อมูลนี้ไหม?';
if (action === EAction.UPDATE) msg = 'ต้องการบันทึกข้อมูลนี้ไหม?';
if (action === EAction.DELETE) msg = 'ต้องการจะลบข้อมูลนี้ไหม?';
if (action === EAction.BACK) msg = 'ต้องการจะออกจากหน้านี้ไหม?';
if (action === EAction.COPY) msg = 'ต้องการคัดลอกข้อมูลนี้ไหม?';
if (action === EAction.REFINANCE) msg = 'ต้องการรีไฟแนนซ์?';
if (action === EAction.RELOAD) msg = 'บันทึกสำเร็จ ต้องการรีเฟรชหน้าจอ?';
const dialog = Swal.fire({
icon: 'warning',
title: `${msg}`,
heightAuto: false,
showCancelButton: true,
confirmButtonText: `${confirmButtonText}`,
cancelButtonText: `${cancelButtonText}`,
});
return from(dialog);
}
generateParamsValue(jsonValue: any) {
let params = '';
Object.keys(jsonValue).forEach(key => {
if (jsonValue[key]) {
params += key + '=' + jsonValue[key] + '&';
}
});
params = params.substring(0, params.length - 1);
return params;
}
isEmpty(data: any) {
switch (data) {
case '':
case 0:
case '0':
case null:
case false:
case 'null':
case 'undefined':
case undefined:
case typeof data === 'undefined':
return true;
default:
return false;
}
}
}

125
src/app/app.shared.ts Normal file
View File

@@ -0,0 +1,125 @@
// ANGULAR
import { LOCALE_ID, NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
// MATERIAL
import { MatTableModule } from "@angular/material/table";
import { MatPaginatorModule } from "@angular/material/paginator";
import { MatSortModule } from "@angular/material/sort";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatCardModule } from "@angular/material/card";
import { MatDatepickerModule } from "@angular/material/datepicker";
import { MatNativeDateModule, MatRippleModule } from "@angular/material/core";
import { MatProgressBarModule } from "@angular/material/progress-bar";
import { MatButtonToggleModule } from "@angular/material/button-toggle";
import { MatGridListModule } from "@angular/material/grid-list";
import { MatExpansionModule } from "@angular/material/expansion";
import { MatAutocompleteModule } from "@angular/material/autocomplete";
import { MatButtonModule } from "@angular/material/button";
import { MatInputModule } from "@angular/material/input";
import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldDefaultOptions, MatFormFieldModule } from "@angular/material/form-field";
import { MatTooltipModule } from "@angular/material/tooltip";
import { MatCheckboxModule } from "@angular/material/checkbox";
import { MatSelectModule } from "@angular/material/select";
import { MatIconModule } from "@angular/material/icon";
import { MatMenuModule } from "@angular/material/menu";
import { MatTabsModule } from "@angular/material/tabs";
import { MatRadioModule } from "@angular/material/radio";
import { MatDialogModule } from "@angular/material/dialog";
import { MatDividerModule } from "@angular/material/divider";
import { MatListModule } from "@angular/material/list";
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
// Module
import { NgSelectModule } from "@ng-select/ng-select";
// UTIL
import { DateDiff, DateFormat, ToDateObjPipe } from "./utils/pipe";
import { CanDirective } from "./utils/can.directive";
import { AllowRoleDirective } from "./utils/allow-role.directives";
import { CurrencyInputMaskDirective } from "./@common/utils/CurrencyInputMask";
import { NumberOnlyDirective } from "./@common/utils/NumberOnlyDirective";
const MAT = [
MatAutocompleteModule,
MatButtonModule,
MatInputModule,
MatRippleModule,
MatFormFieldModule,
MatTooltipModule,
MatSelectModule,
MatCheckboxModule,
MatIconModule,
MatMenuModule,
MatTabsModule,
MatRadioModule,
MatDialogModule,
MatInputModule,
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatProgressSpinnerModule,
MatCardModule,
MatDatepickerModule,
MatNativeDateModule,
MatRippleModule,
MatProgressBarModule,
MatRadioModule,
MatButtonToggleModule,
MatGridListModule,
MatExpansionModule,
MatDialogModule,
MatIconModule,
MatListModule,
MatDividerModule,
MatSlideToggleModule,
];
const appearance: MatFormFieldDefaultOptions = {
appearance: 'outline'
};
const BASE_MODULES = [
CommonModule,
FormsModule,
ReactiveFormsModule
];
const MODULES = [
MatAutocompleteModule,
NgSelectModule,
...MAT
];
const COMPONENTS = [
AllowRoleDirective,
CanDirective,
];
const PIPES = [
ToDateObjPipe,
DateFormat,
DateDiff,
CurrencyInputMaskDirective,
NumberOnlyDirective
];
const PROVIDERS: any = [
{
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
useValue: appearance
},
{ provide: LOCALE_ID, useValue: "en-GB" }
];
@NgModule({
imports: [...BASE_MODULES, ...MODULES],
exports: [...BASE_MODULES, ...MODULES, ...COMPONENTS, ...PIPES],
declarations: [...COMPONENTS, ...PIPES],
providers: [...PROVIDERS],
})
export class AppSharedModule {
}

View File

@@ -0,0 +1,31 @@
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {LoginComponent} from './login/login.component';
import {AuthComponent} from './auth.component';
const routes: Routes = [
{
path: '',
component: AuthComponent,
children: [
{path: '', redirectTo: 'login', pathMatch: 'full'},
{path: 'login', component: LoginComponent},
],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AuthRoutingModule {
}
export const AuthRoutingComponents = [
AuthComponent,
LoginComponent,
];

View File

@@ -0,0 +1,3 @@
<router-outlet></router-outlet>

View File

@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-auth',
templateUrl: './auth.component.html',
styles: []
})
export class AuthComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import {AppSharedModule} from '../app.shared';
import {AuthRoutingModule, AuthRoutingComponents} from './auth-routing.module';
@NgModule({
declarations: [
...AuthRoutingComponents,
],
imports: [
AppSharedModule,
AuthRoutingModule
]
})
export class AuthModule { }

View File

@@ -0,0 +1,37 @@
<div class="auth">
<div class="auth-wrap">
<div class="auth-logo">
<img src="./assets/images/logo-b.png" />
</div>
<div class="auth-card">
<div class="auth-heading">Sign in for Admin</div>
<form #ngf="ngForm" (ngSubmit)="onSubmit(ngf)">
<div class="auth-card-body">
<mat-form-field>
<input matInput type="text" name="username" [(ngModel)]="dataForm.username" #username="ngModel"
placeholder="Username" required>
<i matSuffix class="bi bi-person-circle"></i>
<mat-error *ngIf="isFieldValid(ngf, username)">กรุณากรอกข้อมูล</mat-error>
</mat-form-field>
<div style="height: 10px;"></div>
<mat-form-field>
<input matInput type="password" name="password" [(ngModel)]="dataForm.password"
#password="ngModel" placeholder="Password" required>
<i matSuffix class="bi bi-key"></i>
<mat-error *ngIf="isFieldValid(ngf, password)">กรุณากรอกข้อมูล</mat-error>
</mat-form-field>
</div>
<div class="auth-card-footer">
<button type="submit" class="btn btn-primary w-full"> เข้าสู่ระบบ</button>
</div>
<div class="auth-card-action flex items-center">
<div class="ml-auto mt-1">
<mat-checkbox name="remember" [(ngModel)]="dataForm.remember">บันทึกรหัสผ่าน</mat-checkbox>
</div>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,61 @@
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {AppService} from '../../app.service';
import {lastValueFrom} from "rxjs";
import {API, EAction, EText} from "../../@config/app";
import { environment } from "../../../environments/environment";
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styles: []
})
export class LoginComponent implements OnInit {
apiUrl: string = API.login;
dataForm: any = {};
isLoading = false;
constructor(
private router: Router,
private appService: AppService,
private route: ActivatedRoute
) {
}
ngOnInit() {
if (!environment.production) {
this.dataForm = {
username : 'admin',
password : 'password@1',
}
}
}
async onSubmit(form: any) {
if (!form.valid) return false;
const dataForm = {
username: this.dataForm.username,
password: this.dataForm.password,
userType: 'ADMIN'
};
try {
console.log(this.apiUrl)
const result = await lastValueFrom(this.appService.post(this.apiUrl, dataForm));
this.appService.setAuth(result.data);
this.appService.setToken(result.accessToken);
return this.router.navigate(['/pages']);
} catch (err) {
return this.appService.message(EAction.ERROR, EText.NO_DATA);
}
}
public isFieldValid(form: any, field: any) {
return field.errors && (field.dirty || field.touched || form.submitted);
}
}

View File

@@ -0,0 +1,11 @@
<div class="panel-header">
PAGE
</div>
<div class="main-content">
<div class="card">
<div class="card-body"></div>
</div>
</div>

View File

@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-comingsoon',
templateUrl: './comingsoon.component.html',
styles: []
})
export class ComingsoonComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@@ -0,0 +1,144 @@
<div class="layouts" [class.active-sidebar]="isToggleSidebar">
<div class="main-sidebar">
<div class="main-sidebar-container">
<div class="main-menu">
<section class="main-menu-heading">
<div class="sm:hidden btn-toggle ">
<button type="button" class="btn-icon" (click)="toggleSidebar()">
<i class="bi bi-justify"></i>
</button>
</div>
<img class="logo-main" src="./assets/images/logo.png" />
<img class="logo-icon" src="./assets/images/logo-icon.png" />
</section>
<nav class="main-menu-nav">
<ng-template #menuTemplate let-menus>
<div *ngFor="let item of menus; let i = index">
<ng-container *ngIf="roleCheck(item.permission)">
<ng-container [ngSwitch]="item.type">
<ng-container *ngSwitchCase="'heading'">
<div class="menu-heading">{{ item.name }}</div>
</ng-container>
<ng-container *ngSwitchCase="'link'">
<a class="menu-item" [routerLink]="[item.link]">
<div routerLinkActive="active" matRipple
class="menu-item-wrap flex items-center">
<div class="menu-item-icon"><i
class="{{ item.icon || 'icon-sm bi bi-circle-fill' }}"></i>
</div>
<div class="menu-item-text">{{ item.name }}</div>
</div>
</a>
</ng-container>
<ng-container *ngSwitchCase="'collapsable'">
<a class="menu-item menu-main-item" (click)="item.collapsed = !item.collapsed">
<div matRipple class="menu-item-wrap flex items-center">
<div class="menu-item-icon"><i
class="{{ item.icon || 'icon-sm bi bi-circle-fill' }}"></i>
</div>
<div class="menu-item-text">{{ item.name }}</div>
<div class="menu-item-action ml-auto">
<i *ngIf="item.collapsed" class="bi bi-chevron-down"></i>
<i *ngIf="!item.collapsed" class="bi bi-chevron-up"></i>
</div>
</div>
</a>
<div *ngIf="item.children && item.collapsed" class="menu-item-children">
<ng-container
*ngTemplateOutlet="menuTemplate; context:{ $implicit: item.children }"></ng-container>
</div>
</ng-container>
</ng-container>
</ng-container>
</div>
</ng-template>
<ng-container *ngTemplateOutlet="menuTemplate; context:{ $implicit: menus }"></ng-container>
<a class="menu-item menu-main-item" (click)="logout()">
<div matRipple class="menu-item-wrap flex items-center">
<div class="menu-item-icon"><i class="bi bi-power"></i></div>
<div class="menu-item-text">ออกจากระบบ</div>
</div>
</a>
</nav>
</div>
</div>
</div>
<div class="main-content ">
<header class="main-content-header flex items-center">
<div class="sm:block hidden btn-toggle">
<button type="button" class="btn-icon" (click)="toggleSidebar()">
<i class="bi bi-justify"></i>
</button>
</div>
<div class="sm:hidden font-bold flex flex-col">
<span class="text-sm">CATHAY INTERNATIONAL</span>
<span class="text-xs">Shopping Center</span>
</div>
<div class="title-mobile">
<div class="text"></div>
<div class="img">
<img src="./assets/images/logo.png" />
</div>
</div>
<div class="ml-auto flex items-center ">
<div class="top-bar-user flex items-center" [matMenuTriggerFor]="menu">
<div class="top-bar-user-icon">
<i class="bi bi-person-circle"></i>
</div>
<div class="top-bar-user-text md:hidden">
<div class="name">{{auth?.name || '-'}}</div>
<div class="role"> {{auth?.email || '-' }}</div>
</div>
</div>
</div>
</header>
<div class="main-content-toolbar flex items-center">
<div *ngFor="let item of breadcrumb; let i = index">
<ng-container *ngIf="i === 0"> {{item.name}}</ng-container>
<ng-container *ngIf="i > 0 && i +1 !== breadcrumb.length">
<span>></span>
<a [routerLink]="[item.link]"> {{item.name}} </a>
</ng-container>
<ng-container *ngIf="i +1 === breadcrumb.length">
<span>></span>
<a> {{item.name}} </a>
</ng-container>
</div>
</div>
<div class="main-content-container">
<div id="main-top"></div>
<router-outlet (deactivate)="onDeactivate()"></router-outlet>
</div>
<div class="main-overlay">
<div class="overlay-wrap" (click)="closeSidebar()"></div>
<button type="button" class="btn-icon close-icon" (click)="closeSidebar()"><i
class="bi bi-x-lg"></i></button>
</div>
</div>
</div>
<mat-menu #menu="matMenu">
<!-- <a mat-menu-item [routerLink]="['/pages', 'account','change-password' ]" style="color: #83A5B9">-->
<!-- <i class="bi bi-key"></i>-->
<!-- เปลี่ยนรหัสผ่าน-->
<!-- </a>-->
<a mat-menu-item (click)="logout()">
<i class="bi bi-power"></i>
ออกจากระบบ
</a>
</mat-menu>

View File

@@ -0,0 +1,127 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AppService } from '../../app.service';
import { MENU } from "../../@config/menus";
import { lastValueFrom } from "rxjs";
import { API } from "../../@config/app";
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-pages-layouts',
templateUrl: './layouts.component.html',
styles: [],
})
export class PagesLayoutsComponent implements OnInit {
menus = MENU;
isToggleSidebar = false;
innerWidth: any;
auth: any = {};
isCollapsed: any = [];
breadcrumb: any = [];
permissionCheck = false;
permission: any = [];
constructor(
private app: AppService,
private router: Router,
private activatedRoute: ActivatedRoute,
public changeDetectorRef: ChangeDetectorRef,
) {
}
async ngOnInit() {
this.onCollapsed();
this.getBreadcrumb();
await this.initAuth();
this.changeDetectorRef.markForCheck()
}
async initAuth() {
this.auth = this.app.auth();
if (!this.permissionCheck) {
const users = await lastValueFrom(this.app.get(`${API.users}/getById/${this.auth.id}`));
this.permission = users.permission;
this.permissionCheck = true;
}
}
roleCheck(perm: string){
if(!environment.production) return true
return this.permission.includes(perm)
}
getBreadcrumb() {
this.breadcrumb = [];
let router: any = this.router.url;
router = router.split('/');
this.mapBreadcrumb(router, this.menus)
this.changeDetectorRef.markForCheck()
}
mapBreadcrumb(router: any, items: any) {
items.map((item: any) => {
this.addItemBreadcrumb(router, item);
if (item.children) this.mapBreadcrumb(router, item.children);
});
}
addItemBreadcrumb(router: any, item: any) {
const data = {
name: item.name,
link: item.link,
}
if (router[2]) {
if (item.link === router[2]) this.breadcrumb.push(data);
}
if (router[3]) {
if (item.link === `${router[2]}/${router[3]}`) this.breadcrumb.push(data);
}
if (router[4]) {
if (item.link === `${router[2]}/${router[3]}/${router[4]}`) this.breadcrumb.push(data);
}
if (router[5]) {
if (item.link === `${router[2]}/${router[3]}/${router[4]}/${router[5]}`) this.breadcrumb.push(data);
}
if (router[6]) {
if (item.link === `${router[2]}/${router[3]}/${router[4]}/${router[5]}/${router[6]}`) this.breadcrumb.push(data);
}
}
onCollapsed() {
let router: any = this.router.url;
router = router.split('/');
this.menus.forEach((item: any, i: number) => {
// item.collapsed = false;
if (item.type === 'collapsable') {
if (router.includes(item.link)) {
item.collapsed = true;
}
}
});
}
logout() {
this.app.logout();
return this.router.navigate(['/auth']);
}
toggleSidebar() {
this.isToggleSidebar = !this.isToggleSidebar;
}
closeSidebar() {
this.isToggleSidebar = false;
}
onDeactivate() {
this.ngOnInit();
}
}

View File

@@ -0,0 +1,27 @@
<div class="dialog-main">
<div class="dialog-header">
<h2>{{title}}</h2>
</div>
<div class="dialog-body">
<ng-container *ngIf="viewType === 'images' ">
<div class="tac">
<img src="{{viewPath}}/{{dialog.images}}" style="max-width: 100%" alt="">
</div>
</ng-container>
<ng-container *ngIf="viewType === 'vdo' ">
<div class="video">
<video controls #videoPlayer>
<source src="{{viewPath}}/{{dialog.images}}" type="video/mp4" />
Browser not supported
</video>
</div>
</ng-container>
</div>
<div class="dialog-footer">
<button type="button" (click)="onClose()" class="btn btn-dialog-close">ปิด</button>
</div>
</div>

View File

@@ -0,0 +1,63 @@
import { ChangeDetectorRef, Component, Inject, OnInit } from "@angular/core";
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
import { BasePopupComponent } from "../../../@common/base/base-popup.component";
import { API, EAction, STORAGE } from "../../../@config/app";
import { AppService } from "../../../app.service";
import { Router } from "@angular/router";
import { NgIf } from "@angular/common";
@Component({
selector: "app-attachments-view",
templateUrl: "./attachments-view.component.html",
styleUrls: [],
imports: [
NgIf
],
standalone: true
})
export class AttachmentsViewComponent extends BasePopupComponent implements OnInit {
title = "รายละเอียด";
api = API;
dataFile: any;
storage: any = STORAGE;
viewType = 'images';
viewPath = STORAGE.images;
constructor(
public dialogRef: MatDialogRef<AttachmentsViewComponent>,
@Inject(MAT_DIALOG_DATA) public dialog: any,
public changeDetectorRef: ChangeDetectorRef,
public appService: AppService,
public router: Router
) {
super();
}
async ngOnInit() {
if (this.dialog.title) this.title = this.dialog.title;
this.onAttachmentsType(this.dialog.images);
if (this.dialog.storage === 'products') this.viewPath = STORAGE.products;
}
onAttachmentsType(image?: any) {
if (!image) return;
const ext = image.substring(image.lastIndexOf('.'), image.length);
if (['.png','.jpg', '.JPEG'].includes(ext)) this.viewType = 'images' ;
if (['.mp4'].includes(ext)) this.viewType = 'vdo' ;
}
async onClose() {
this.dialogRef.close(EAction.GET);
}
}

View File

@@ -0,0 +1,97 @@
<div class="dialog-main">
<div class="dialog-header">
<h2>{{title}}</h2>
</div>
<div class="dialog-body">
<div class="card card-form-panel mt-2">
<div class="card-header ">
<div class="">ข้อมูล Packet</div>
</div>
<div class="card-body">
<ng-container *ngFor="let item of packetDetail; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-2 md:col-span-4">
<label *ngIf="i === 0">รหัส</label>
<mat-form-field>
<input matInput name="code-{{i}}" #code="ngModel" [(ngModel)]="item.code" required disabled>
</mat-form-field>
</div>
<div class="col-span-6 md:col-span-8">
<label *ngIf="i === 0">รายการจัดผ่อน</label>
<mat-form-field>
<input appNumberOnly matInput name="name-{{i}}" #name="ngModel" [(ngModel)]="item.name" disabled>
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-6">
<label *ngIf="i === 0">จำนวน</label>
<mat-form-field>
<input appNumberOnly matInput name="value-{{i}}" #value="ngModel" [(ngModel)]="item.value" disabled>
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-6">
<label *ngIf="i === 0">หน่วยนับ</label>
<ng-select placeholder="เลือกหน่วยนับ" name="unitId-{{i}}" #unit="ngModel" [(ngModel)]="item.unit" appendTo="body" disabled>
<ng-option *ngFor="let item of settingInstallmentUnit" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
</div>
</div>
</ng-container>
</div>
</div>
<div class="card card-form-panel mt-6">
<div class="card-header ">
<div class="">โปรโมชั่น</div>
</div>
<div class="card-body">
<div style="height: 10px;"></div>
<div class="grid grid-cols-12 gap-4 md:gap-2 items-center mb-1">
<div class="col-span-4 md:col-span-12">
<ng-select placeholder="" name="promotionId" #promotionId="ngModel" [(ngModel)]="dataView.promotionId" appendTo="body" disabled>
<ng-option *ngFor="let item of promotionData" [value]="item.id">{{item.name}}</ng-option>
</ng-select>
</div>
</div>
<ng-container *ngIf="promotionDetail">
<ng-container *ngFor="let item of promotionDetail; let i = index">
<ng-container *ngIf="item.status">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-8 md:col-span-10 ">
<div *ngIf="i === 0" style="height: 25px;"></div>
<mat-form-field>
<input appNumberOnly matInput name="promotionName-{{i}}" #promotionName="ngModel" [(ngModel)]="item.name" disabled>
</mat-form-field>
</div>
<!-- <div class="col-span-2 md:col-span-6">-->
<!-- <label *ngIf="i === 0">จำนวน</label>-->
<!-- <mat-form-field>-->
<!-- <input appNumberOnly matInput name="promotionValue-{{i}}" #promotionValue="ngModel" [(ngModel)]="item.value" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div class="col-span-2 md:col-span-6">-->
<!-- <label *ngIf="i === 0">หน่วยนับ</label>-->
<!-- <ng-select placeholder="เลือกหน่วยนับ" name="promotionUnitId-{{i}}" #promotionUnit="ngModel" [(ngModel)]="item.unit" appendTo="body" disabled>-->
<!-- <ng-option *ngFor="let item of unitData" [value]="item">{{item}}</ng-option>-->
<!-- </ng-select>-->
<!-- </div>-->
</div>
</div>
</ng-container>
</ng-container>
</ng-container>
<div style="height: 20px;"></div>
</div>
</div>
</div>
<div class="dialog-footer">
<button type="button" (click)="onClose()" class="btn btn-dialog-close">ปิด</button>
</div>
</div>

View File

@@ -0,0 +1,75 @@
import { ChangeDetectorRef, Component, Inject, OnInit } from "@angular/core";
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
import { BasePopupComponent } from "../../../@common/base/base-popup.component";
import { API, EAction, STORAGE } from "../../../@config/app";
import { AppService } from "../../../app.service";
import { Router } from "@angular/router";
import { NgForOf, NgIf } from "@angular/common";
import { AppSharedModule } from "../../../app.shared";
import { FormsModule } from "@angular/forms";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { NgSelectModule } from "@ng-select/ng-select";
import { lastValueFrom } from "rxjs";
import deepCopy from "../../../@common/utils/DeepCopy";
@Component({
selector: "app-packet-view",
templateUrl: "./packet-view.component.html",
styleUrls: [],
imports: [
NgIf,
AppSharedModule,
FormsModule,
MatFormFieldModule,
MatInputModule,
NgForOf,
NgSelectModule
],
standalone: true
})
export class PacketViewComponent extends BasePopupComponent implements OnInit {
title = "รายละเอียด Packet";
api : any = API;
dataView: any = {};
storage: any = STORAGE;
settingInstallmentUnit: any = ["บาท", "%"];
packetDetail: any = [];
promotionDetail: any = [];
promotionData: any = [];
unitData: any = ["%"];
constructor(
public dialogRef: MatDialogRef<PacketViewComponent>,
@Inject(MAT_DIALOG_DATA) public dialog: any,
public changeDetectorRef: ChangeDetectorRef,
public appService: AppService,
public router: Router
) {
super();
}
async ngOnInit() {
if (this.dialog.title) this.title = this.dialog.title;
this.promotionData = await lastValueFrom(this.appService.get(`${this.api.promotion}?showAll=true&status=true`));
const data = deepCopy(this.dialog.data);
this.dataView = data;
this.packetDetail = data.packetDetail;
this.promotionDetail = data.promotion?.promotionDetail;
}
async onClose() {
this.dialogRef.close(EAction.GET);
}
}

View File

@@ -0,0 +1,28 @@
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {Appraisal1stTimeIndexComponent} from './index/appraisal-1st-time-index.component';
import {Appraisal1stTimeDoComponent} from "./do/appraisal-1st-time-do.component";
import { Appraisal1stTimeHistoryComponent } from "./history/appraisal-1st-time-history.component";
import { Appraisal1stTimePdfComponent } from "./pdf/appraisal-1st-time-pdf.component";
const routes: Routes = [
{path: '', component: Appraisal1stTimeIndexComponent},
{path: 'do/:action', component: Appraisal1stTimeDoComponent},
{path: 'do/:action/:id', component: Appraisal1stTimeDoComponent},
{path: 'history', component: Appraisal1stTimeHistoryComponent},
{path: 'history/pdf/:id', component: Appraisal1stTimePdfComponent},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RoutingModule {
}
export const RoutingComponents = [
Appraisal1stTimeIndexComponent,
Appraisal1stTimeDoComponent,
Appraisal1stTimeHistoryComponent,
Appraisal1stTimePdfComponent,
];

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import {RoutingComponents, RoutingModule} from './appraisal-1st-time-routing.module';
import {AppSharedModule} from "../../../app.shared";
import { NgOptimizedImage } from "@angular/common";
@NgModule({
declarations: [
...RoutingComponents,
],
imports: [
AppSharedModule,
RoutingModule,
NgOptimizedImage
]
})
export class Appraisal1stTimeModule {}

View File

@@ -0,0 +1,391 @@
<form class="main-form" #ngf="ngForm" (ngSubmit)="onSubmit(ngf)">
<form #ngfCalculate="ngForm" (ngSubmit)="onCalculate(ngfCalculate)">
<div class="grid grid-cols-12 gap-4 md:gap-2 h-full">
<div class="col-span-6 md:col-span-12">
<div class="card card-form-panel card-form-panel-blue h-full">
<div class="card-header ">
<div class="card-title"> กรอกข้อมูลการจัดผ่อน <span class="color-red">(ราคาล่าสุด {{dataView.latestPrice | number : '1.2-2'}} บาท)</span></div>
</div>
<div class="card-body form-input-list">
<div style="height: 20px;"></div>
<div class="grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> เลือก Packet</label>
</div>
<div class="col-span-4 md:col-span-12">
<ng-select placeholder="เลือก Packet" name="packetId" #packetId="ngModel" [(ngModel)]="dataForm.packetId" (ngModelChange)="onChangePacket($event)" appendTo="body" required>
<ng-option *ngFor="let item of packets" [value]="item.id">{{item.name}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit" *ngIf="dataForm.packetId">
<i (click)="onPacketView()" class="bi bi-search color-main cursor-pointer"></i>
</div>
</div>
</div>
<div class="grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> วันที่เริ่มจัดผ่อน</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input
matInput
name="startDate"
#startDate="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataForm.startDate"
[matDatepicker]="dpkName"
readonly
required
/>
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit"></div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ราคา (Price)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="price" #price="ngModel" [(ngModel)]="dataForm.price" (ngModelChange)="onChange($event, 'price')" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำแม่ค้า</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="sellerDeposit" #sellerDeposit="ngModel" [(ngModel)]="dataForm.sellerDeposit" (ngModelChange)="onChange($event, 'sellerDeposit')" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำ CMFS</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="deposit" #deposit="ngModel" [(ngModel)]="dataForm.deposit" (ngModelChange)="onChange($event, 'deposit')" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> เงินต้นคงเหลือ (Total)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="principalBalanceTotal" #principalBalanceTotal="ngModel" [(ngModel)]="dataForm.principalBalanceTotal" required disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ต้องการผ่อน (Term)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput type="number" name="wantToInstallmentTerm" #wantToInstallmentTerm="ngModel" [(ngModel)]="dataForm.wantToInstallmentTerm" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">งวด</div>
</div>
</div>
<div class="action">
<button type="button" class="btn btn-back" (click)="onCalculateReset()">ล้างข้อมูล</button>
<button type="submit" class="btn btn-submit">คำนวน</button>
</div>
</div>
</div>
</div>
<div class="col-span-6 md:col-span-12 ">
<div class="card card-form-panel card-form-panel-blue h-full">
<div class="card-header ">
<div class="card-title"> รายละเอียดค่าใช้จ่ายในการโอนเงิน</div>
</div>
<div class="card-body form-input-list">
<div style="height: 20px;"></div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำ CMFS Deposit</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="cmfsDeposit" #cmfsDeposit="ngModel" [(ngModel)]="dataForm.cmfsDeposit" (ngModelChange)="onChange($event, 'cmfsDeposit')" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> หัก เงินมัดจำแม่ค้า </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="lessSellerDeposit" #lessSellerDeposit="ngModel" [(ngModel)]="dataForm.lessSellerDeposit" (ngModelChange)="onChange($event, 'lessSellerDeposit')">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Packing </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusPacking" #plusPacking="ngModel" [(ngModel)]="dataForm.plusPacking" (ngModelChange)="onChange($event, 'plusPacking')" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Luxury handbag </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusLuxuryHandbag" #plusLuxuryHandbag="ngModel" [(ngModel)]="dataForm.plusLuxuryHandbag" (ngModelChange)="onChange($event, 'plusLuxuryHandbag')" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Bank fee, Insurance , Storage</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusBankFee" #plusBankFee="ngModel" [(ngModel)]="dataForm.plusBankFee" (ngModelChange)="onChange($event, 'plusBankFee')" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ส่วนลด </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="discount" #discount="ngModel" [(ngModel)]="dataForm.discount" (ngModelChange)="onChange($event, 'discount')" >
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> สรุปยอดโอน </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="transferSummary" #transferSummary="ngModel" [(ngModel)]="dataForm.transferSummary" disabled >
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
<div style="height: 20px;"></div>
<div class="card card-table mb-6">
<div class="card-body">
<div *ngIf="!dataForm.quotationDetail?.[0]" class="no-data color-red font-bold"> กรุณากรอกข้อมูลการจัดผ่อนเพื่อแสดงตารางผ่อนชำระ</div>
<div class="table-wrap" *ngIf="dataForm.quotationDetail?.[0]">
<table class="tables">
<thead>
<tr>
<th>งวดที่</th>
<th>กำหนดจ่ายวันที่ <br>Due date</th>
<th>เงินต้น <br>Principle</th>
<th>ดอกเบี้ย(บาท) <br>Interest Total</th>
<th>Bank fee, <br>Insurance ,Storage</th>
<th>รวมยอดจ่ายต่อเดือน <br>Total payment</th>
<th>เงินต้นคงเหลือ <br>Principle Total</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let item of dataForm.quotationDetail; let i = index">
<tr>
<td class="text-center">{{item.installment }}</td>
<td class="text-center">{{item.dueDate | date : 'dd/MM/YYYY'}}</td>
<td class="text-center">{{item.principle | number: '1.0-0'}}</td>
<td class="text-center">{{item.interestTotal | number: '1.0-0'}}</td>
<td class="text-center">{{item.fee | number: '1.0-0'}}</td>
<td class="text-center"><span class="b-color-green">{{item.totalPayment | number: '1.0-0'}}</span></td>
<td class="text-center"><span class="b-color-orange" *ngIf="item.principleTotal">{{item.principleTotal | number: '1.0-0'}}</span></td>
</tr>
</ng-container>
<tr>
<td colspan="2" class="text-right"><b>รวม</b></td>
<td class="text-center">{{dataForm.principleSum | number: '1.0-0'}}</td>
<td class="text-center">{{dataForm.interestTotalSum | number: '1.0-0'}}</td>
<td class="text-center">{{dataForm.feeSum | number: '1.0-0'}}</td>
<td class="text-center"><span class="b-color-green">{{dataForm.totalPaymentSum | number: '1.0-0'}}</span></td>
<td class="text-center"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลลูกค้า</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-3 md:col-span-12 ">
<mat-label>คำนำหน้า</mat-label>
<ng-select placeholder="เลือกคำนำหน้า" name="prefix" #prefix="ngModel" [(ngModel)]="dataForm.customerPrefix" appendTo="body" >
<ng-option *ngFor="let item of prefixData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-3 md:col-span-12 ">
<mat-label>ชื่อลูกค้า</mat-label>
<mat-form-field>
<input matInput name="customerFirstName" #customerFirstName="ngModel" [(ngModel)]="dataForm.customerFirstName" >
</mat-form-field>
</div>
<div class="col-span-3 md:col-span-12 ">
<mat-label>นามสกุล</mat-label>
<mat-form-field>
<input matInput name="customerLastName" #customerLastName="ngModel" [(ngModel)]="dataForm.customerLastName">
</mat-form-field>
</div>
<div class="col-span-3 md:col-span-12 ">
<mat-label>เบอร์โทร</mat-label>
<mat-form-field>
<input matInput name="customerPhone" #customerPhone="ngModel" [(ngModel)]="dataForm.customerPhone">
</mat-form-field>
</div>
<!-- <div class="col-span-3 md:col-span-12 ">-->
<!-- <mat-label>BOM</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="productNo" #productNo="ngModel" [(ngModel)]="dataForm.productNo" required>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<div class="col-span-3 md:col-span-12 ">
<mat-label>ประเภทรหัส</mat-label>
<ng-select placeholder="ประเภทรหัส" name="typeCode" #typeCode="ngModel" [(ngModel)]="dataForm.typeCode" appendTo="body" required>
<ng-option *ngFor="let item of typeCodeData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<!-- <div class="col-span-3 md:col-span-12 ">-->
<!-- <mat-label>เลขที่ใบเสนอราคา</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="quotationNo" #quotationNo="ngModel" [(ngModel)]="dataForm.quotationNo" required>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div class="col-span-9 md:hidden"></div>-->
<div class="col-span-3 md:col-span-12 ">
<mat-label>ชื่อพนักงาน</mat-label>
<mat-form-field>
<input matInput name="userFullName" #userFullName="ngModel" [(ngModel)]="dataForm.userFullName" required disabled>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">รูปสินค้าจากลูกค้า</div>
<div class="ml-4">
<input hidden type="file" accept="image/*" #productImages (change)="onAttachments($event, 'products')" />
<button type="button" class="btn btn-sm btn-success-o" (click)="productImages.click()">เพิ่มรูปภาพสินค้า</button>
</div>
</div>
</div>
<div class="card-body">
<div class="list-images">
<div class=" grid grid-cols-12 gap-2 md:gap-2 items-center">
<ng-container *ngFor="let item of attachments; let i = index">
<div class="col-span-2 md:col-span-4">
<div class="flex justify-center items-center list-images-item">
<div class="list-images-action">
<i *ngIf="dataForm.coverImage !== item" (click)="dataForm.coverImage = item"
matTooltip="ใช้ทำเอกสาร" class="bi bi-star color-main cursor-pointer select-none"></i>
<i *ngIf="dataForm.coverImage === item" class="bi bi bi-star-fill color-main cursor-pointer select-none"></i>
<i (click)="onRemoveAttachments(i, item)" class="bi bi-x-circle color-red cursor-pointer select-none"></i>
</div>
<img src="{{storage.products}}/{{item}}" alt="">
</div>
</div>
</ng-container>
</div>
</div>
</div>
</div>
<div class="main-form-action text-right">
<button type="submit" class="btn btn-submit">บันทึก</button>
<button type="button" class="btn btn-back" (click)="onAction('back')">ยกเลิก</button>
</div>
</form>

View File

@@ -0,0 +1,309 @@
import {ChangeDetectorRef, Component, OnInit} from "@angular/core";
import {API, EAction, EText, PREFIX, STORAGE, TYPE_CODE} from "../../../../@config/app";
import {AppService} from "../../../../app.service";
import {lastValueFrom} from "rxjs";
import {BaseFormComponent} from "../../../../@common/base/base-form.component";
import {ActivatedRoute, Router} from "@angular/router";
import {IProduct} from "../../../../app.interface";
import {addMonths, differenceInDays} from "date-fns";
import deepCopy from "../../../../@common/utils/DeepCopy";
import {PacketViewComponent} from "../../../@popup/packet-view/packet-view.component";
import {MatDialog} from "@angular/material/dialog";
@Component({
selector: "app-appraisal-1st-time-do",
templateUrl: "./appraisal-1st-time-do.component.html",
styleUrls: []
})
export class Appraisal1stTimeDoComponent extends BaseFormComponent implements OnInit {
override dataForm: any = {};
dataView: IProduct = {};
auth: any = {};
title = "";
api: any = API;
storage: any = STORAGE;
addItemNumber: number = 1;
attachments: any = [];
settings: any = [];
packets: any = [];
packetData: any = {};
promotionDetail: any = [];
IN01: any = {};
IN05: any = {};
prefixData = PREFIX;
typeCodeData = TYPE_CODE;
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
public changeDetectorRef: ChangeDetectorRef,
public appService: AppService,
private packetDialog: MatDialog,
) {
super();
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.ids = params["id"];
this.action = params["action"];
this.auth = this.appService.auth();
this.settings = await lastValueFrom(this.appService.get(`${this.api.settings}?showAll=true&status=true&orderBy=code&sort=asc&codeIn=IN01,IN02,IN03,IN04,IN05`));
this.packets = await lastValueFrom(this.appService.get(`${API.packet}?showAll=true&status=true`));
if (this.ids) await this.getData();
});
}
async onAction(action: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.BACK));
if (!sweetalert.isConfirmed) return;
if (action === "back") return this.router.navigate(["/pages/appraisal/1st-time"]);
return;
}
async getData() {
if (!this.ids) this.appService.message(EAction.INFO, EText.NO_DATA);
try {
this.dataForm = {};
this.dataView = await lastValueFrom(this.appService.get(`${this.api.products}/getById/${this.ids}`));
this.attachments = this.dataView.images ? this.dataView.images?.split(",") : [];
this.dataForm.startDate = new Date();
this.dataForm.cmfsDeposit = 0;
this.dataForm.deposit = 0;
this.dataForm.plusPacking = 0;
this.dataForm.plusLuxuryHandbag = 0;
this.dataForm.plusBankFee = 0;
this.dataForm.settingCmfsDeposit = 0;
this.dataForm.settingInterestRate = 0;
this.dataForm.discount = 0;
this.dataForm.sellerDeposit = 0;
this.dataForm.lessSellerDeposit = 0;
// this.dataForm.productNo = this.dataView.code;
this.dataForm.productName = this.dataView.name;
this.dataForm.coverImage = this.dataView.coverImage;
this.dataForm.productId = this.dataView.id;
this.dataForm.productSize = this.dataView.size;
this.dataForm.productWeight = this.dataView.weight;
this.dataForm.productColor = this.dataView.color;
this.dataForm.productYear = this.dataView.year;
this.dataForm.productPrice = this.dataView.price;
this.dataForm.productLatestPrice = this.dataView.latestPrice;
this.dataForm.productBrandName = this.dataView.masterProductBrand?.name;
this.dataForm.productMeasurement = this.dataView.productMeasurement;
this.dataForm.userFullName = this.auth.name;
this.changeDetectorRef.detectChanges();
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onChangePacket(value: string) {
this.dataForm.plusPacking = 0;
this.dataForm.plusLuxuryHandbag = 0;
this.dataForm.plusBankFee = 0;
this.dataForm.settingCmfsDeposit = 0;
this.dataForm.settingInterestRate = 0;
if (!value) return;
this.packetData = await lastValueFrom(this.appService.get(`${API.packet}/getById/${value}`));
if (!this.packetData.packetDetail) return;
this.dataForm.plusPacking = this.packetData.packetDetail.find((f: { code: string; }) => f.code === "IN02").value;
this.dataForm.plusLuxuryHandbag = this.packetData.packetDetail.find((f: { code: string; }) => f.code === "IN03").value;
this.dataForm.plusBankFee = this.packetData.packetDetail.find((f: { code: string; }) => f.code === "IN04").value;
this.dataForm.settingCmfsDeposit = this.packetData.packetDetail.find((f: { code: string; }) => f.code === "IN01").value;
this.dataForm.settingInterestRate = this.packetData.packetDetail.find((f: { code: string; }) => f.code === "IN05").value;
this.promotionDetail = this.packetData?.promotion?.promotionDetail;
this.onChange(this.dataForm.price, 'price');
this.dataForm.quotationDetail = [];
}
onChange($event: any, key?: string) {
if (key) this.dataForm[key] = $event;
this.changeDetectorRef.detectChanges();
if (key === "price") {
const IN01 = Number(this.dataForm.settingCmfsDeposit) / 100;
this.dataForm.price = this.dataForm.price ? this.dataForm.price : 0;
this.dataForm.deposit = IN01 * this.dataForm.price;
this.dataForm.cmfsDeposit = IN01 * this.dataForm.price;
}
this.dataForm.sellerDeposit = this.dataForm.sellerDeposit ? this.dataForm.sellerDeposit : 0;
this.dataForm.deposit = this.dataForm.deposit ? this.dataForm.deposit : 0;
if (key === "sellerDeposit") {
this.dataForm.lessSellerDeposit = this.dataForm.sellerDeposit;
}
if (key === "deposit") {
this.dataForm.cmfsDeposit = this.dataForm.deposit;
}
if (key === "cmfsDeposit") {
this.dataForm.deposit = this.dataForm.cmfsDeposit;
}
this.dataForm.principalBalanceTotal = this.dataForm.price - this.dataForm.deposit;
this.dataForm.transferSummary = Number(this.dataForm.cmfsDeposit) +
Number(this.dataForm.plusPacking) +
Number(this.dataForm.plusLuxuryHandbag) +
Number(this.dataForm.plusBankFee) -
Number(this.dataForm.lessSellerDeposit) -
Number(this.dataForm.discount);
this.changeDetectorRef.detectChanges();
}
async onCalculate(form: any) {
if (!form.valid) return false;
this.dataForm.quotationDetail = [];
const principle = this.dataForm.principalBalanceTotal / this.dataForm.wantToInstallmentTerm;
let principleTotalBefore = Number(this.dataForm.principalBalanceTotal);
let startDate = this.dataForm.startDate;
const IN05 = Number(this.dataForm.settingInterestRate) / 100;
for (let i = 0; i < this.dataForm.wantToInstallmentTerm; i++) {
let item = {
installment: i + 1,
dueDate: addMonths(this.dataForm.startDate, i + 1),
principle: Number(principle),
interestTotal: 0,
interestRate: this.promotionDetail?.[i].value,
isInterest: this.promotionDetail?.[i].status,
fee: Number(this.dataForm.plusBankFee),
totalPayment: 0,
principleTotal: 0,
interestPerDays: 0
};
const interestPerDays = differenceInDays(item.dueDate, startDate) + 1;
item.interestTotal = principleTotalBefore * IN05 * interestPerDays / 365;
item.interestPerDays = interestPerDays;
if (item.isInterest) item.interestTotal = 0;
item.totalPayment = item.principle + item.interestTotal + item.fee;
item.principleTotal = principleTotalBefore - item.principle;
startDate = item.dueDate;
principleTotalBefore = item.principleTotal;
console.log(item)
this.dataForm.quotationDetail.push(item);
this.dataForm.principleSum = 0;
this.dataForm.interestTotalSum = 0;
this.dataForm.feeSum = 0;
this.dataForm.feeSum = 0;
this.dataForm.totalPaymentSum = 0;
this.dataForm.quotationDetail.map((item: any) => {
this.dataForm.principleSum += Number(item.principle);
this.dataForm.interestTotalSum += Number(item.interestTotal);
this.dataForm.feeSum += Number(item.fee);
this.dataForm.totalPaymentSum += Number(item.totalPayment);
});
this.dataForm.principleSum = Math.round(this.dataForm.principleSum);
}
console.log("quotationDetail", this.dataForm.quotationDetail);
return;
}
async onCalculateReset() {
return await this.getData();
}
async onSubmit(form: any) {
if (!form.valid) return false;
if (!this.dataForm.quotationDetail?.[0]) return this.appService.message(EAction.ERROR, "กรุณากรอกข้อมูลการจัดผ่อนเพื่อแสดงตารางผ่อนชำระ");
this.dataForm.images = this.attachments?.[0] ? this.attachments.join(",") : null;
if (this.action === EAction.CREATE) return await this.onCreate();
if (this.action === EAction.UPDATE) return await this.onUpdate();
return;
}
async onCreate() {
try {
await lastValueFrom(this.appService.post(this.api.quotation, this.dataForm));
await this.appService.message(EAction.SUCCESS, EText.CREATE);
await this.router.navigate(["/pages/appraisal/1st-time"]);
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onUpdate() {
try {
await lastValueFrom(this.appService.post(`${this.api.quotation}/update/${this.ids}`, this.dataForm));
await this.appService.message(EAction.SUCCESS, EText.UPDATE);
await this.router.navigate(["/pages/setting/products"]);
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onAttachments($event: any, type: any) {
const file = $event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("ref", type);
formData.append("file", file);
try {
const res = await lastValueFrom(this.appService.post(`${this.api.attachments}/products`, formData));
if (!this.attachments[0]) {
this.dataForm.coverImage = res.fileName;
}
this.attachments.push(res.fileName);
console.log(this.attachments, res);
this.changeDetectorRef.detectChanges();
} catch (e) {
this.appService.message(EText.ERROR);
}
}
async onRemoveAttachments(i: number, fileName: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
// await lastValueFrom(this.appService.delete(`${this.api.attachments}/deleteByName`, fileName));
this.attachments?.splice(i, 1);
if (!this.attachments[0]) {
this.dataForm.coverImage = null;
}
this.changeDetectorRef.detectChanges();
}
async onPacketView() {
const dialogConfig = deepCopy(this.dialogConfig);
dialogConfig.data.action = EAction.POPUP;
dialogConfig.data.title = 'รายละเอียด Packet';
dialogConfig.data.data = this.packetData;
const dialogRef = this.packetDialog.open(PacketViewComponent, dialogConfig);
const afterClosed = await lastValueFrom(dialogRef.afterClosed());
}
}

View File

@@ -0,0 +1,123 @@
<div class="card card-table">
<div class="card-filter text-right">
<div class="card-filter-section grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-5 md:col-span-12 md:order-2">
<mat-form-field>
<i matTextPrefix class="bi bi-search"></i>
<input matInput name="keyword" #keyword="ngModel" [(ngModel)]="dataFilter.keyword" (ngModelChange)="onFilter($event)">
</mat-form-field>
</div>
<div class="col-span-7 md:col-span-12 md:order-1">
<div class="card-header-action">
<button type="button" class="btn btn-export" (click)="onExport()">Export</button>
</div>
</div>
</div>
<div class="card-filter-section grid grid-cols-12 gap-4 items-center md:gap-2 ">
<div class="col-span-3 md:col-span-6">
<mat-form-field>
<input
matInput
name="startDate"
#startDate="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataFilter.createdDate"
[matDatepicker]="dpkName"
readonly
(ngModelChange)="getData()"
/>
<!-- <mat-icon matSuffix (click)="clearDate($event)">clear</mat-icon>-->
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-9 md:col-span-12 ">
<div class="flex w-full ">
<div class="">จำนวนทั้งหมด {{totalItem}} รายการ</div>
<div class="pl-2 pr-2">|</div>
<div class="">ค้นหาจำนวน {{totalOfElement}} รายการ</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-wrap">
<table class="table table-main" mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<ng-container matColumnDef="quotationNo">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>เลขที่ใบเสนอราคา</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.quotationNo}}</td>
</ng-container>
<ng-container matColumnDef="customerFirstName">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>ชื่อลูกค้า</th>
<td mat-cell *matCellDef="let item" width="150" class="">
<ng-container *ngIf="item.customerId"> {{item.customer?.prefix}}{{item.customer?.firstName}} {{item.customer?.lastName}}</ng-container>
<ng-container *ngIf="!item.customerId">{{item.customerFirstName}} {{item.customerLastName}}</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="productNo">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>BOM</th>
<td mat-cell *matCellDef="let item" width="150" class="tac">{{item.productNo}}</td>
</ng-container>
<ng-container matColumnDef="productName">
<th mat-header-cell *matHeaderCellDef class="tal">Model</th>
<td mat-cell *matCellDef="let item" class="" style="min-width: 200px;">{{item.productName }}</td>
</ng-container>
<ng-container matColumnDef="price">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-green"> {{item.price | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="wantToInstallmentTerm">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ระยะเวลาผ่อน</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.wantToInstallmentTerm }} งวด</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่บันทึก</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.createdDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef class="tac">สถานะ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.status === 'paid' " class="status status-active">ชำระแล้ว</div>
<div *ngIf="item.status === 'pending'" class="status status-disabled">รอชำระ</div>
</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef width="80">Action</th>
<td mat-cell *matCellDef="let item">
<div class="action flex justify-center">
<div class="item">
<i class="bi bi-filetype-pdf color-main" (click)="onAction(item.id)"></i>
</div>
<div class="item" *ngIf="item.status === 'pending'">
<i class="bi bi-trash3 color-red" (click)="onDelete(item.id)"></i>
</div>
</div>
</td>
</ng-container>
</table>
<div *ngIf="dataSourceCount === 0" class="no-data"></div>
</div>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons (page)="getData($event)"></mat-paginator>
</div>
</div>

View File

@@ -0,0 +1,91 @@
import { Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API, EAction, EText } from "../../../../@config/app";
import { Router } from "@angular/router";
import generateParamsValue from "../../../../@common/utils/GenerateParamsValue";
@Component({
selector: "app-appraisal-1st-time-index",
templateUrl: "./appraisal-1st-time-history.component.html",
styleUrls: []
})
export class Appraisal1stTimeHistoryComponent extends BaseListComponent implements OnInit {
pageTitle = "ประวัติการสร้างใบเสนอราคา";
apiUrl: string = API.quotation;
api: any = API;
displayedColumns: string[] = ["action","price", "quotationNo", "customerFirstName", "productName", "wantToInstallmentTerm", "createdDate", "status"];
masterProductCategory: any = [];
masterProductBrand: any = [];
filterKeyword: Subject<string> = new Subject<string>();
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService
) {
super();
this.filterKeyword.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(model => {
this.getData();
});
}
async ngOnInit() {
await this.getData();
}
onAction(id?: any) {
if (id) return this.router.navigate(["/pages/appraisal/1st-time/history/pdf", id]);
return this.router.navigate(["/pages/appraisal/1st-time/do", "create"]);
}
async getData($event?: any) {
try {
this.dataFilter.keywordColumn = "quotationNo,productNo,customerFirstName,customerLastName,price";
const dataSource = await lastValueFrom(this.appService.get(this.setParams(this.apiUrl, $event)));
this.dataSource = this.setDataSource<any>(dataSource);
} catch (e) {
this.dataSource = [];
}
}
onFilter($event?: any) {
this.filterKeyword.next($event);
}
clearDate($event?: any) {
$event.stopPropagation();
this.dataFilter.createdDate = null;
}
async onSort($event: any) {
this.dataFilter.orderBy = $event.active;
this.dataFilter.sort = $event.direction;
await this.getData();
console.log($event);
}
async onDelete(ids: any) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
try {
await lastValueFrom(this.appService.delete(this.apiUrl, ids));
await this.appService.message(EAction.SUCCESS, EText.DELETE);
await this.getData(this.getCurrentPage());
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
onExport() {
const filter = generateParamsValue(this.dataFilter);
const url = `${API.quotation}/export-history?${filter ? '&' + filter : '' }`;
window.open(url);
}
}

View File

@@ -0,0 +1,122 @@
<div class="card card-table">
<div class="card-filter text-right">
<div class="card-filter-section grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-5 md:col-span-12 md:order-2">
<mat-form-field>
<i matTextPrefix class="bi bi-search"></i>
<input matInput name="keyword" #keyword="ngModel" [(ngModel)]="dataFilter.keyword" (ngModelChange)="onFilter($event)">
</mat-form-field>
</div>
<div class="col-span-7 md:col-span-12 md:order-1">
<div class="card-header-action">
<button type="button" class="btn btn-create" (click)="onAction()">
<i class="bi bi-plus"></i>
ประวัติการสร้างใบเสนอราคา
</button>
</div>
</div>
</div>
<div class="card-filter-section grid grid-cols-12 gap-4 items-center md:gap-2 ">
<div class="col-span-3 md:col-span-6">
<ng-select name="categoryId" #categoryId="ngModel" [(ngModel)]="dataFilter.categoryId" (ngModelChange)="getData()" appendTo="body" required placeholder="เลือกหมวดหมู่">
<ng-option *ngFor="let item of masterProductCategory" [value]="item.id">{{item.name}}</ng-option>
</ng-select>
</div>
<div class="col-span-3 md:col-span-6">
<ng-select placeholder="Choose Brand" name="brandId" #brandId="ngModel" [(ngModel)]="dataFilter.brandId" (ngModelChange)="getData()" appendTo="body" required>
<ng-option *ngFor="let item of masterProductBrand" [value]="item.id">{{item.name}}</ng-option>
</ng-select>
</div>
<div class="col-span-6 md:col-span-12 ">
<div class="flex w-full justify-end md:justify-start">
<div class="">จำนวนทั้งหมด {{totalItem}} รายการ</div>
<div class="pl-2 pr-2">|</div>
<div class="">ค้นหาจำนวน {{totalOfElement}} รายการ</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-wrap">
<table class="table table-main" mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<ng-container matColumnDef="code">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>BOM</th>
<td mat-cell *matCellDef="let item" width="150" class="tac">{{item.code}}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef class="tal">Model</th>
<td mat-cell *matCellDef="let item" class="" style="min-width: 200px;">
<!-- <div class="flex items-center">-->
<!-- <div class="image-td"><img *ngIf="item.coverImage" src="{{storage.products}}/{{item.coverImage}}" alt=""></div>-->
<!-- <div class="">{{item.name }}</div>-->
<!-- </div>-->
{{item.name }}
</td>
</ng-container>
<ng-container matColumnDef="brandId">
<th mat-header-cell *matHeaderCellDef class="tal" mat-sort-header>Brand</th>
<td mat-cell *matCellDef="let item" class="">{{item?.masterProductBrand.name }}</td>
</ng-container>
<ng-container matColumnDef="size">
<th mat-header-cell *matHeaderCellDef class="tal" width="150">Main</th>
<td mat-cell *matCellDef="let item" class="">{{item.size }}</td>
</ng-container>
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef class="tal" width="150">น้ำหนัก</th>
<td mat-cell *matCellDef="let item" class="">{{item.weight }}</td>
</ng-container>
<ng-container matColumnDef="color">
<th mat-header-cell *matHeaderCellDef class="tal" width="150">Color</th>
<td mat-cell *matCellDef="let item" class="">{{item.color }}</td>
</ng-container>
<ng-container matColumnDef="year">
<th mat-header-cell *matHeaderCellDef class="tal" width="150">Year</th>
<td mat-cell *matCellDef="let item" class="">{{item.year }}</td>
</ng-container>
<ng-container matColumnDef="price">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>ราคา</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-orange"> {{item.price | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="latestPrice">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>ราคาล่าสุด</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-green"> {{item.latestPrice | number : '1.2-2' }}</div>
</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef width="80">สร้างใบเสนอราคา</th>
<td mat-cell *matCellDef="let item">
<div class="action flex justify-center">
<div class="item">
<i class="bi bi-file-earmark-text icon-doc" (click)="onAction(item.id)"></i>
</div>
</div>
</td>
</ng-container>
</table>
</div>
<div *ngIf="dataSourceCount === 0" class="no-data"></div>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons (page)="getData($event)"></mat-paginator>
</div>
</div>

View File

@@ -0,0 +1,71 @@
import { Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API } from "../../../../@config/app";
import { Router } from "@angular/router";
@Component({
selector: "app-appraisal-1st-time-index",
templateUrl: "./appraisal-1st-time-index.component.html",
styleUrls: []
})
export class Appraisal1stTimeIndexComponent extends BaseListComponent implements OnInit {
pageTitle = "สินค้า";
apiUrl: string = API.products;
api: any = API;
displayedColumns: string[] = ["action", "price", "latestPrice","code", "name", "brandId", "size",'weight', "color", "year" ];
masterProductCategory: any = [];
masterProductBrand: any = [];
filterKeyword: Subject<string> = new Subject<string>();
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService
) {
super();
this.filterKeyword.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(model => {
this.getData();
});
}
async ngOnInit() {
this.masterProductCategory = await lastValueFrom(this.appService.get(`${this.api.masterProductCategory}?showAll=true&status=true`));
this.masterProductBrand = await lastValueFrom(this.appService.get(`${this.api.masterProductBrand}?showAll=true&status=true`));
await this.getData();
}
onAction(id?: any) {
if (id) return this.router.navigate(["/pages/appraisal/1st-time/do", "create", id]);
return this.router.navigate(["/pages/appraisal/1st-time/history"]);
}
async getData($event?: any) {
try {
this.dataFilter.keywordColumn = "name,price,latestPrice";
const dataSource = await lastValueFrom(this.appService.get(this.setParams(this.apiUrl, $event)));
this.dataSource = this.setDataSource<any>(dataSource);
} catch (e) {
this.dataSource = [];
}
}
onFilter($event?: any) {
this.filterKeyword.next($event);
}
async onSort($event: any) {
this.dataFilter.orderBy = $event.active;
this.dataFilter.sort = $event.direction;
await this.getData();
console.log($event);
}
}

View File

@@ -0,0 +1,3 @@
<mat-progress-bar *ngIf="!pdfView" mode="indeterminate"></mat-progress-bar>
<iframe *ngIf="pdfView" [src]="pdfView"></iframe>

View File

@@ -0,0 +1,105 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import { lastValueFrom } from "rxjs";
import { AppService } from "../../../../app.service";
import { API, STORAGE } from "../../../../@config/app";
import { ActivatedRoute, Router } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
import { BaseFormComponent } from "../../../../@common/base/base-form.component";
import { IQuotation } from "../../../../@common/interface/Quotation";
import { format, parseISO } from "date-fns";
@Component({
selector: "app-appraisal-1st-time-index",
templateUrl: "./appraisal-1st-time-pdf.component.html",
styleUrls: []
})
export class Appraisal1stTimePdfComponent extends BaseFormComponent implements OnInit {
pageTitle = "ใบเสนอราคา";
apiUrl: string = API.quotation;
api: any = API;
dataView: any;
pdfView: any;
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
public changeDetectorRef: ChangeDetectorRef,
public appService: AppService,
private sanitizer: DomSanitizer
) {
super();
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.ids = params["id"];
await this.getData();
});
}
async getData() {
try {
const quotation : IQuotation = await lastValueFrom(this.appService.get(`${this.api.quotation}/getById/${this.ids}`));
const startDate = quotation.startDate ? format(parseISO(quotation.startDate), "dd/MM/yyyy") : null;
const customerPrefix = quotation.customerPrefix ? quotation.customerPrefix : '';
const customerName = quotation.customerId ? `${quotation.customer?.prefix} ${quotation.customer?.firstName} ${quotation.customer?.lastName}` :
`${customerPrefix} ${quotation.customerFirstName} ${quotation.customerLastName}`;
const data = {
doc_no: quotation.quotationNo,
product_code: quotation.productNo,
type_code: quotation.typeCode,
customer_name: customerName,
phone_no: quotation.customerPhone,
installment_start_date: startDate,
picture: `${STORAGE.products}/${quotation.coverImage}`,
price: Number(quotation.price),
seller_deposit: Number(quotation.sellerDeposit),
cmfs_deposit: Number(quotation.cmfsDeposit),
total_balance: Number(quotation.principalBalanceTotal),
installment: Number(quotation.wantToInstallmentTerm),
packing: Number(quotation.plusPacking),
luxury_handbag_authentication: Number(quotation.plusLuxuryHandbag),
bankfee_insurance_storage: Number(quotation.plusBankFee),
transfer_amount: Number(quotation.transferSummary),
discount: Number(quotation.discount),
data: [],
total1: 0,
total2: 0,
total3: 0,
total4: 0
}
const quotationDetail: any = [];
quotation.quotationDetail?.map(item => {
const dueDate = item.dueDate ? format(parseISO(item.dueDate), "dd/MM/yyyy") : null;
const map = {
due_date: dueDate,
principle: Number(item.principle),
interest_total: Number(item.interestTotal),
bank_fee: Number(item.fee),
total_payment: Number(item.totalPayment),
principle_total: Number(item.principleTotal)
}
quotationDetail.push(map);
})
data.data = quotationDetail;
const pdf = await lastValueFrom(this.appService.post(`${this.api.quotationReport}/pdf`, data, { responseType: "arraybuffer" }));
const url = URL.createObjectURL(new Blob([pdf], { type: "application/pdf" }));
this.pdfView = this.sanitizer.bypassSecurityTrustResourceUrl(url);
} catch (e) {
console.log(e);
}
}
}

View File

@@ -0,0 +1,28 @@
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {Appraisal2ndTimeIndexComponent} from './index/appraisal-2nd-time-index.component';
import {Appraisal2ndTimeDoComponent} from "./do/appraisal-2nd-time-do.component";
import { Appraisal2ndTimeHistoryComponent } from "./history/appraisal-2nd-time-history.component";
import { Appraisal2ndTimePdfComponent } from "./pdf/appraisal-2nd-time-pdf.component";
const routes: Routes = [
{path: '', component: Appraisal2ndTimeIndexComponent},
{path: 'list', component: Appraisal2ndTimeIndexComponent},
{path: 'list/:action', component: Appraisal2ndTimeIndexComponent},
{path: 'do/:action', component: Appraisal2ndTimeDoComponent},
{path: 'do/:action/:id', component: Appraisal2ndTimeDoComponent},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RoutingModule {
}
export const RoutingComponents = [
Appraisal2ndTimeIndexComponent,
Appraisal2ndTimeDoComponent,
Appraisal2ndTimeHistoryComponent,
Appraisal2ndTimePdfComponent,
];

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import {RoutingComponents, RoutingModule} from './appraisal-2nd-time-routing.module';
import {AppSharedModule} from "../../../app.shared";
import { NgOptimizedImage } from "@angular/common";
@NgModule({
declarations: [
...RoutingComponents,
],
imports: [
AppSharedModule,
RoutingModule,
NgOptimizedImage
]
})
export class Appraisal2ndTimeModule {}

View File

@@ -0,0 +1,262 @@
<form class="main-form" #ngf="ngForm" (ngSubmit)="onSubmit(ngf)">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลลูกค้า</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 ">
<div class="col-span-4 md:col-span-12 ">
<mat-label>ค้นหาเลขบัตรประชาชน</mat-label>
<ng-select placeholder="ค้นหาเลขบัตรประชาชน" name="filterIdCard" #filterIdCard="ngModel" [(ngModel)]="dataFilter.idCard" (ngModelChange)="onChangeFilter($event)" appendTo="body" >
<ng-option *ngFor="let item of customer" [value]="item.idCard">{{item.idCard}} : {{item.prefix}} {{item.firstName}} {{item.lastName}}</ng-option>
</ng-select>
</div>
<div class="col-span-8 md:hidden"></div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>คำนำหน้า</mat-label>
<ng-select placeholder="เลือกคำนำหน้า" name="prefix" #prefix="ngModel" [(ngModel)]="dataForm.customer.prefix" appendTo="body" required>
<ng-option *ngFor="let item of prefixData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อลูกค้า</mat-label>
<mat-form-field>
<input matInput name="firstName" #firstName="ngModel" [(ngModel)]="dataForm.customer.firstName" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>นามสกุล</mat-label>
<mat-form-field>
<input matInput name="lastName" #lastName="ngModel" [(ngModel)]="dataForm.customer.lastName" >
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>เพศ</mat-label>
<ng-select placeholder="เลือกเพศ" name="gender" #gender="ngModel" [(ngModel)]="dataForm.customer.gender" appendTo="body" >
<ng-option *ngFor="let item of genderData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เบอร์โทร</mat-label>
<mat-form-field>
<input matInput name="phone" #phone="ngModel" [(ngModel)]="dataForm.customer.phone" >
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เลขบัตรประชาชน</mat-label>
<mat-form-field>
<input matInput name="idCard" #idCard="ngModel" [(ngModel)]="dataForm.customer.idCard" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<div style="height: 20px;"></div>
<div class="flex items-center">
<div class="">
<input hidden type="file" accept="image/*" #idCardImages (change)="onAttachmentsIdCard($event, 'idcard')" />
<button type="button" class="btn btn-icon-upload color-main" (click)="idCardImages.click()">
<ng-container *ngIf="!dataForm.customer.idCardImage"> <i class="bi bi-plus-circle "></i> สำเนาบัตรประชาชน</ng-container>
<ng-container *ngIf="dataForm.customer.idCardImage"> <i class="bi bi-pencil" matTooltip="แก้ไขสำเนาบัตรประชาชน"></i> </ng-container>
</button>
</div>
<div style="padding-top: 4px;">
<ng-container *ngIf="dataForm.customer.idCardImage">
<div class="cursor-pointer" (click)="onAttachmentsView('images')"><i class="bi bi-search"></i> ดูสำเนาบัตรประชาชน</div></ng-container>
</div>
</div>
</div>
<div class="col-span-8 md:hidden"></div>
<div class="col-span-12 md:col-span-12 ">
<mat-label>ที่อยู่ตามบัตรประชาชน</mat-label>
<mat-form-field>
<input matInput name="address" #address="ngModel" [(ngModel)]="dataForm.customer.address" (ngModelChange)="onChangeAddress('address', $event)">
</mat-form-field>
</div>
<div class="col-span-12 md:col-span-12 ">
<mat-label>ที่อยู่ในการจัดส่ง</mat-label>
<label class="inline-flex items-center cursor-pointer select-none ml-2">
<input type="checkbox" name="isAddress" [(ngModel)]="dataForm.customer.isAddress" (ngModelChange)="onChangeAddress('isAddress', $event)">
<span style="padding-left: 2px;">ใช้ที่อยู่ตามบัตรประชาชน</span>
</label>
<mat-form-field>
<input matInput name="deliveryAddress" #deliveryAddress="ngModel" [(ngModel)]="dataForm.customer.deliveryAddress" [disabled]="dataForm.customer.isAddress">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>E-mail</mat-label>
<mat-form-field>
<input matInput name="email" #email="ngModel" [(ngModel)]="dataForm.customer.email">
</mat-form-field>
</div>
<div class="col-span-8 md:col-span-12 ">
<mat-label>อาชีพ</mat-label>
<mat-form-field>
<input matInput name="occupation" #occupation="ngModel" [(ngModel)]="dataForm.customer.occupation">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID Line</mat-label>
<mat-form-field>
<input matInput name="line" #line="ngModel" [(ngModel)]="dataForm.customer.line">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>Facebook</mat-label>
<mat-form-field>
<input matInput name="facebook" #facebook="ngModel" [(ngModel)]="dataForm.customer.facebook">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>IG</mat-label>
<mat-form-field>
<input matInput name="ig" #ig="ngModel" [(ngModel)]="dataForm.customer.ig">
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="text-right mt-4 ">
<mat-checkbox name="depositChecked" #depositChecked="ngModel" [(ngModel)]="dataForm.depositChecked">ชำระเงินเพิ่ม</mat-checkbox>
</div>
<div class="card card-table mb-6">
<div class="card-body">
<div class="table-wrap" >
<table class="tables ">
<thead>
<tr>
<th>BOM</th>
<th>Brand</th>
<th>Model</th>
<th>ราคาสินค้า</th>
<th>จำนวนเงินมัดจำ</th>
<th>ค่ามัดจำเพิ่ม</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center">
<mat-form-field>
<input matInput name="productNo" #productNo="ngModel" [(ngModel)]="dataForm.productNo" required>
</mat-form-field>
</td>
<td class="text-center">{{dataForm.productBrandName}}</td>
<td class="text-center">{{dataForm.productName }}</td>
<td class="text-center">{{dataForm.price | number : '1.2-2'}}</td>
<td class="text-center">
<div class="b-color-orange">{{dataForm.deposit | number : '1.2-2'}}</div>
</td>
<td>
<mat-form-field>
<input matInput name="paymentAmount" #paymentAmount="ngModel" [(ngModel)]="dataForm.paymentAmount" [required]="dataForm.depositChecked" [disabled]="!dataForm.depositChecked">
</mat-form-field>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">รูปสินค้าจากลูกค้า</div>
<div class="ml-4">
<input hidden type="file" accept="image/*" #productImages (change)="onAttachments($event, 'products')" />
<button type="button" class="btn btn-sm btn-success-o" (click)="productImages.click()">เพิ่มรูปภาพสินค้า</button>
</div>
</div>
</div>
<div class="card-body">
<div class="list-images">
<div class=" grid grid-cols-12 gap-2 md:gap-2 items-center">
<ng-container *ngFor="let item of attachments; let i = index">
<div class="col-span-2 md:col-span-4">
<div class="flex justify-center items-center list-images-item">
<div class="list-images-action">
<i *ngIf="dataForm.coverImage !== item" (click)="dataForm.coverImage = item"
matTooltip="ใช้ทำเอกสาร" class="bi bi-star color-main cursor-pointer select-none"></i>
<i *ngIf="dataForm.coverImage === item" class="bi bi bi-star-fill color-main cursor-pointer select-none"></i>
<i (click)="onRemoveAttachments(i, item)" class="bi bi-x-circle color-red cursor-pointer select-none"></i>
</div>
<img src="{{storage.products}}/{{item}}" alt="">
</div>
</div>
</ng-container>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">อุปกรณ์</div>
</div>
</div>
<div class="card-body">
<ng-container *ngFor="let item of equipmentData; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-1 md:col-span-2 text-right">
<div *ngIf="i === 0" style="height: 25px;"></div>
<input type="checkbox" name="isCheck-{{i}}" #isCheck="ngModel" [(ngModel)]="item.isCheck">
</div>
<div class="col-span-9 md:col-span-8 cursor-pointer select-none" (click)="item.isCheck = !item.isCheck">
<div *ngIf="i === 0" style="height: 25px;"></div>
{{item.name}}
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="isEquipmentOther()">
<ng-container *ngFor="let item of equipmentOtherData; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-1 md:col-span-2 text-right">
</div>
<div class="col-span-9 md:col-span-8 cursor-pointer select-none" >
<mat-form-field>
<input matInput name="equipmentOtherItem-{{i}}" #equipmentOtherItem="ngModel" [(ngModel)]="item.value" >
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-2 " >
<i (click)="onAddEquipmentOther()" class="bi bi-plus-circle color-green cursor-pointer select-none mr-2"></i>
<i (click)="onRemoveEquipmentOther(i)" class="bi bi-x-circle color-red cursor-pointer select-none"></i>
</div>
</div>
</div>
</ng-container>
</ng-container>
<div style="height: 40px;"></div>
</div>
</div>
<div class="main-form-action text-right">
<button type="submit" class="btn btn-submit">บันทึก</button>
<button type="button" class="btn btn-back" (click)="onAction('back')">ยกเลิก</button>
</div>
</form>

View File

@@ -0,0 +1,274 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import { API, EAction, EText, GENDER, PREFIX, STORAGE } from "../../../../@config/app";
import { AppService } from "../../../../app.service";
import { lastValueFrom } from "rxjs";
import { BaseFormComponent } from "../../../../@common/base/base-form.component";
import { ActivatedRoute, Router } from "@angular/router";
import { IProduct } from "../../../../app.interface";
import deepCopy from "../../../../@common/utils/DeepCopy";
import { AttachmentsViewComponent } from "../../../@popup/attachments-view/attachments-view.component";
import { MatDialog } from "@angular/material/dialog";
@Component({
selector: "app-appraisal-2nd-time-do",
templateUrl: "./appraisal-2nd-time-do.component.html",
styleUrls: []
})
export class Appraisal2ndTimeDoComponent extends BaseFormComponent implements OnInit {
override dataForm: any = {};
dataView: IProduct = {};
auth: any = {};
title = "";
api: any = API;
storage: any = STORAGE;
addItemNumber: number = 1;
attachments: any = [];
settings: any = [];
IN01: any = {};
IN05: any = {};
prefixData = PREFIX;
genderData = GENDER;
customer : any = [];
equipmentData : any = [];
equipmentOtherData : any = [{value : ''}];
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
public changeDetectorRef: ChangeDetectorRef,
public appService: AppService,
private attachmentsView: MatDialog,
) {
super();
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.ids = params["id"];
this.action = params["action"];
this.auth = this.appService.auth();
this.dataForm.customer = {};
this.dataForm.sellsr = {};
this.defaultEquipmentData();
this.customer = await lastValueFrom(this.appService.get(`${API.customer}?showAll=true`));
if (this.ids) await this.getData();
});
}
async onAction(action: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.BACK));
if (!sweetalert.isConfirmed) return;
if (action === "back") return this.router.navigate(["/pages/appraisal/2nd-time/list", this.action]);
return;
}
async getData() {
if (!this.ids) this.appService.message(EAction.INFO, EText.NO_DATA);
try {
this.dataForm = await lastValueFrom(this.appService.get(`${this.api.quotation}/getById/${this.ids}`));
this.attachments = this.dataForm.images ? this.dataForm.images?.split(",") : [];
this.dataForm.paymentAmount = 0;
this.dataForm.customer = {};
this.dataForm.customer.prefix = this.dataForm.customerPrefix;
this.dataForm.customer.firstName = this.dataForm.customerFirstName;
this.dataForm.customer.lastName = this.dataForm.customerLastName;
this.dataForm.customer.phone = this.dataForm.customerPhone;
if (this.dataForm.customer.isAddress) this.dataForm.customer.deliveryAddress = this.dataForm.customer.address;
if (!this.dataForm.productNo) {
const currentDate = new Date();
let currentMonth: any = new Date().getMonth() + 1;
currentMonth = currentMonth.toString().padStart(2, '0');
let currentYear: any = currentDate.getFullYear() + 543;
currentYear = currentYear.toString().substring(2);
const RUNNING = this.dataForm.id.toString().padStart(6, '0');
this.dataForm.productNo = `${this.dataForm.typeCode}${currentYear}-${currentMonth}-${RUNNING}`;
}
const equipment : any[] = this.dataForm.equipment ? this.dataForm.equipment?.split(",") : [];
const equipmentOther : any[] = this.dataForm.equipmentOther ? this.dataForm.equipmentOther?.split(",") : [];
if (equipment.length) {
this.equipmentData.map((d: any) => {
if (equipment.includes(d.name)) d.isCheck = true;
})
}
if (equipmentOther.length) {
this.equipmentOtherData = [];
equipmentOther.map((d : any) => {
this.equipmentOtherData.push({ value : d});
})
}
this.changeDetectorRef.detectChanges();
} catch (err) {
console.log(err)
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onChangeFilter(value : any) {
if (!value) return await this.getData();
this.dataForm.customer = this.customer.filter((f : any) => f.idCard === value)?.[0];
}
onChangeAddress(key : string, value : any) {
if (key === 'isAddress') this.dataForm.customer.deliveryAddress = value ? this.dataForm.customer.address : '';
if (key === 'address') this.dataForm.customer.deliveryAddress = this.dataForm.customer.isAddress ? value : this.dataForm.customer.deliveryAddress;
}
async onSubmit(form: any) {
if (!form.valid) return false;
if (!this.dataForm.customer.idCardImage) {
return this.appService.message(EAction.ERROR, 'กรุณาอัพโหลดสำเนาบัตรประชาชน');
}
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.CREATE));
if (!sweetalert.isConfirmed) return;
const equipment : any[] = [];
this.equipmentData.map((d : any) => {
if (d.isCheck) equipment.push(d.name);
})
this.dataForm.equipment = equipment?.[0] ? equipment.join(",") : null;
this.dataForm.equipmentOther = null;
if (equipment.includes('อื่นๆ')) {
const equipmentOther : any[] = [];
this.equipmentOtherData.map((d : any) => {
equipmentOther.push(d.value);
})
this.dataForm.equipmentOther = equipmentOther?.[0] ? equipmentOther.join(",") : null;
}
this.dataForm.images = this.attachments?.[0] ? this.attachments.join(",") : null;
return await this.onUpdate();
}
async onUpdate() {
try {
await lastValueFrom(this.appService.post(`${this.api.quotation}/update/${this.ids}`, this.dataForm));
await this.appService.message(EAction.SUCCESS, EText.UPDATE);
await this.router.navigate(["/pages/appraisal/2nd-time/list", this.action]);
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onAttachments($event: any, type: any) {
const file = $event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("ref", type);
formData.append("file", file);
try {
const res = await lastValueFrom(this.appService.post(`${this.api.attachments}/products`, formData));
if (!this.attachments[0]) {
this.dataForm.coverImage = res.fileName;
}
this.attachments.push(res.fileName);
console.log(this.attachments, res);
this.changeDetectorRef.detectChanges();
} catch (e) {
this.appService.message(EText.ERROR);
}
}
async onRemoveAttachments(i: number, fileName: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
// await lastValueFrom(this.appService.delete(`${this.api.attachments}/deleteByName`, fileName));
this.attachments?.splice(i, 1);
if (!this.attachments[0]) {
this.dataForm.coverImage = null;
}
this.changeDetectorRef.detectChanges();
}
async onAttachmentsIdCard($event: any, type: any) {
const file = $event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("ref", type);
formData.append("file", file);
try {
const res = await lastValueFrom(this.appService.post(`${API.attachments}/images`, formData));
this.dataForm.customer.idCardImage = res.fileName;
this.changeDetectorRef.detectChanges();
} catch (e) {
this.appService.message(EText.ERROR);
}
}
async onAttachmentsView(type : any) {
const dialogConfig = deepCopy(this.dialogConfig);
dialogConfig.data.action = EAction.POPUP;
dialogConfig.data.title = 'สำเนาบัตรประชาชน';
dialogConfig.data.type = type;
dialogConfig.data.images = this.dataForm.customer.idCardImage;
const dialogRef = this.attachmentsView.open(AttachmentsViewComponent, dialogConfig);
const afterClosed = await lastValueFrom(dialogRef.afterClosed());
}
defaultEquipmentData() {
this.equipmentData = [
{ name : 'ตัวเปล่า', isCheck : false},
{ name : 'ถุงกระดาษ', isCheck : false},
{ name : 'ถุงผ้า', isCheck : false},
{ name : 'ใบเสร็จ', isCheck : false},
{ name : 'บุ๊ค 1', isCheck : false},
{ name : 'บุ๊ค 2', isCheck : false},
{ name : 'ดอกคามิเลีย', isCheck : false},
{ name : 'ริบบิ้น', isCheck : false},
{ name : 'ถุงกันฝน', isCheck : false},
{ name : 'การ์ด', isCheck : false},
{ name : 'สายกระเป๋า', isCheck : false},
{ name : 'ใบเซอร์', isCheck : false},
{ name : 'พวงกุญแจแม่ และลูก', isCheck : false},
{ name : 'กล่อง', isCheck : false},
{ name : 'สายนาฬิกา', isCheck : false},
{ name : 'ใบรับประกัน', isCheck : false},
{ name : 'ใบตรวจ', isCheck : false},
{ name : 'อื่นๆ', isCheck : false},
]
}
isEquipmentOther() {
return this.equipmentData.find((f : any) => f.name === 'อื่นๆ' && f.isCheck === true)
}
onAddEquipmentOther() {
this.equipmentOtherData.push({value : ''});
}
onRemoveEquipmentOther(i : number) {
if (i === 0) return;
this.equipmentOtherData?.splice(i, 1);
this.changeDetectorRef.detectChanges();
}
}

View File

@@ -0,0 +1,118 @@
<div class="card card-table">
<div class="card-filter text-right">
<div class="card-filter-section grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-5 md:col-span-12 md:order-2">
<mat-form-field>
<i matTextPrefix class="bi bi-search"></i>
<input matInput name="keyword" #keyword="ngModel" [(ngModel)]="dataFilter.keyword" (ngModelChange)="onFilter($event)">
</mat-form-field>
</div>
</div>
<div class="card-filter-section grid grid-cols-12 gap-4 items-center md:gap-2 ">
<div class="col-span-3 md:col-span-6">
<mat-form-field>
<input
matInput
name="startDate"
#startDate="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataFilter.createdDate"
[matDatepicker]="dpkName"
readonly
(ngModelChange)="getData()"
/>
<!-- <mat-icon matSuffix (click)="clearDate($event)">clear</mat-icon>-->
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-9 md:col-span-12 ">
<div class="flex w-full ">
<div class="">จำนวนทั้งหมด {{totalItem}} รายการ</div>
<div class="pl-2 pr-2">|</div>
<div class="">ค้นหาจำนวน {{totalOfElement}} รายการ</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-wrap">
<table class="table table-main" mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<ng-container matColumnDef="quotationNo">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>เลขที่ใบเสนอราคา</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.quotationNo}}</td>
</ng-container>
<ng-container matColumnDef="customerFirstName">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>ชื่อลูกค้า</th>
<td mat-cell *matCellDef="let item" width="150" class="">
<ng-container *ngIf="item.customerId"> {{item.customer?.prefix}}{{item.customer?.firstName}} {{item.customer?.lastName}}</ng-container>
<ng-container *ngIf="!item.customerId">{{item.customerFirstName}} {{item.customerLastName}}</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="productNo">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>BOM</th>
<td mat-cell *matCellDef="let item" width="150" class="tac">{{item.productNo}}</td>
</ng-container>
<ng-container matColumnDef="productName">
<th mat-header-cell *matHeaderCellDef class="tal">Model</th>
<td mat-cell *matCellDef="let item" class="" style="min-width: 200px;">{{item.productName }}</td>
</ng-container>
<ng-container matColumnDef="price">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-green"> {{item.price | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="wantToInstallmentTerm">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ระยะเวลาผ่อน</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.wantToInstallmentTerm }} งวด</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่บันทึก</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.createdDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef class="tac">สถานะ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.status === 'paid' " class="status status-active">ชำระแล้ว</div>
<div *ngIf="item.status === 'pending'" class="status status-disabled">รอชำระ</div>
</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef width="80">Action</th>
<td mat-cell *matCellDef="let item">
<div class="action flex justify-center">
<div class="item">
<i class="bi bi-filetype-pdf color-main" (click)="onAction(item.id)"></i>
</div>
<div class="item" *ngIf="item.status === 'pending'">
<i class="bi bi-trash3 color-red" (click)="onDelete(item.id)"></i>
</div>
</div>
</td>
</ng-container>
</table>
<div *ngIf="dataSourceCount === 0" class="no-data"></div>
</div>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons (page)="getData($event)"></mat-paginator>
</div>
</div>

View File

@@ -0,0 +1,86 @@
import { Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API, EAction, EText } from "../../../../@config/app";
import { Router } from "@angular/router";
@Component({
selector: "app-appraisal-1st-time-index",
templateUrl: "./appraisal-2nd-time-history.component.html",
styleUrls: []
})
export class Appraisal2ndTimeHistoryComponent extends BaseListComponent implements OnInit {
pageTitle = "ประวัติการสร้างใบเสนอราคา";
apiUrl: string = API.quotation;
api: any = API;
displayedColumns: string[] = ["action", "quotationNo", "customerFirstName", "productNo", "productName", "price", "wantToInstallmentTerm", "createdDate", "status"];
masterProductCategory: any = [];
masterProductBrand: any = [];
filterKeyword: Subject<string> = new Subject<string>();
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService
) {
super();
this.filterKeyword.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(model => {
this.getData();
});
}
async ngOnInit() {
await this.getData();
}
onAction(id?: any) {
if (id) return this.router.navigate(["/pages/appraisal/1st-time/history/pdf", id]);
return this.router.navigate(["/pages/appraisal/1st-time/do", "create"]);
}
async getData($event?: any) {
try {
this.dataFilter.keywordColumn = "quotationNo,productNo,customerFirstName,customerLastName";
const dataSource = await lastValueFrom(this.appService.get(this.setParams(this.apiUrl, $event)));
this.dataSource = this.setDataSource<any>(dataSource);
} catch (e) {
this.dataSource = [];
}
}
onFilter($event?: any) {
this.filterKeyword.next($event);
}
clearDate($event?: any) {
$event.stopPropagation();
this.dataFilter.createdDate = null;
}
async onSort($event: any) {
this.dataFilter.orderBy = $event.active;
this.dataFilter.sort = $event.direction;
await this.getData();
console.log($event);
}
async onDelete(ids: any) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
try {
await lastValueFrom(this.appService.delete(this.apiUrl, ids));
await this.appService.message(EAction.SUCCESS, EText.DELETE);
await this.getData(this.getCurrentPage());
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
}

View File

@@ -0,0 +1,186 @@
<div class="card card-table">
<div class="card-filter text-right">
<div class="card-filter-section grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-5 md:col-span-12 md:order-2">
<mat-form-field>
<i matTextPrefix class="bi bi-search"></i>
<input matInput name="keyword" #keyword="ngModel" [(ngModel)]="dataFilter.keyword" (ngModelChange)="onFilter($event)">
</mat-form-field>
</div>
<div class="col-span-7 md:col-span-12 md:order-1">
<button type="button" class="btn btn-export" (click)="onExport()">Export</button>
</div>
</div>
<div class="card-filter-section grid grid-cols-12 gap-4 items-center md:gap-2 ">
<div class="col-span-6 md:col-span-12">
<div class="tabs-btn">
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'wait'}" (click)="onTabs('wait')">รอประเมิน</button>
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'evaluated'}" (click)="onTabs('evaluated')">ประเมินแล้ว</button>
</div>
</div>
<div class="col-span-6 md:col-span-12 ">
<div class="flex w-full justify-end md:justify-start">
<div class="">จำนวนทั้งหมด {{totalItem}} รายการ</div>
<div class="pl-2 pr-2">|</div>
<div class="">ค้นหาจำนวน {{totalOfElement}} รายการ</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-wrap">
<table class="table table-main" mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<ng-container matColumnDef="quotationNo">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>เลขที่ใบเสนอราคา</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.quotationNo}}</td>
</ng-container>
<ng-container matColumnDef="customerFirstName">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>ชื่อลูกค้า</th>
<td mat-cell *matCellDef="let item" width="150" class="">
<ng-container *ngIf="item.customerId"> {{item.customer?.prefix}}{{item.customer?.firstName}} {{item.customer?.lastName}}</ng-container>
<ng-container *ngIf="!item.customerId">{{item.customerFirstName}} {{item.customerLastName}}</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="productNo">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>BOM</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.productNo}}</td>
</ng-container>
<ng-container matColumnDef="productName">
<th mat-header-cell *matHeaderCellDef class="tal">Model</th>
<td mat-cell *matCellDef="let item" class="" style="min-width: 220px;">{{item.productName }}</td>
</ng-container>
<ng-container matColumnDef="productBrandName">
<th mat-header-cell *matHeaderCellDef class="tal">Brand</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productBrandName }}</td>
</ng-container>
<ng-container matColumnDef="productSize">
<th mat-header-cell *matHeaderCellDef class="tal">Main</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productSize }}</td>
</ng-container>
<ng-container matColumnDef="productWeight">
<th mat-header-cell *matHeaderCellDef class="tal" >น้ำหนัก</th>
<td mat-cell *matCellDef="let item" class="">{{item.productWeight }}</td>
</ng-container>
<ng-container matColumnDef="productColor">
<th mat-header-cell *matHeaderCellDef class="tal">Color</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productColor }}</td>
</ng-container>
<ng-container matColumnDef="productYear">
<th mat-header-cell *matHeaderCellDef class="tal">Year</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productYear }}</td>
</ng-container>
<ng-container matColumnDef="price">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-green"> {{item.price | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="deposit">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนเงินมัดจำ</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-orange"> {{item.deposit | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="sellerDeposit2ndTime">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>เงินมัดจำเพิ่ม</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-red" *ngIf="item.sellerDeposit2ndTime"> {{item.sellerDeposit2ndTime | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="wantToInstallmentTerm">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ระยะเวลาผ่อน</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.wantToInstallmentTerm }} งวด</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่บันทึก</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.createdDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef class="tac">สถานะ</th>
<td mat-cell *matCellDef="let item" class="tac" style="min-width: 100px;">
<div *ngIf="item.status === 'paid' " class="status status-active">ชำระแล้ว</div>
<div *ngIf="item.status === 'pending'" class="status status-disabled">รอชำระ</div>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ประเภทการชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.type === 'deposit' " class="status-text status-deposit">ค่ามัดจำ</div>
<div *ngIf="item.type === 'installment'" class="status-text status-installment">ผ่อนสินค้า</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentType">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ประเภทการชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.paymentType === 'deposit' " class="status-text status-deposit">ค่ามัดจำ</div>
<div *ngIf="item.paymentType === 'installment'" class="status-text status-installment">ผ่อนสินค้า</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentMethod">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>วิธีชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.paymentMethod === 'transfer' " class="status-text status-transfer">โอนเงิน</div>
<div *ngIf="item.paymentMethod === 'cash'" class="status-text status-cash">เงินสด</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentAmountAll">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="tac">
<div class="b-color-green">{{item.paymentAmountAll | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef width="80">Action</th>
<td mat-cell *matCellDef="let item">
<div class="action flex justify-center">
<div class="item" *ngIf="item.status2ndTime === 'wait' " >
<i class="bi bi-pencil-square icon-edit" (click)="onAction(item.id)"></i>
</div>
<!-- <div class="item" *ngIf="item.status === 'paid' ">-->
<!-- <i class="bi bi-filetype-pdf color-main" (click)="onAction(item.id)"></i>-->
<!-- </div>-->
</div>
</td>
</ng-container>
</table>
<div *ngIf="dataSource?.length === 0" class="no-data"></div>
</div>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons (page)="getData($event)"></mat-paginator>
</div>
</div>

View File

@@ -0,0 +1,121 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API, EAction, EStatusQuotation, EText } from "../../../../@config/app";
import { ActivatedRoute, Router } from "@angular/router";
import generateParamsValue from "../../../../@common/utils/GenerateParamsValue";
@Component({
selector: "app-appraisal-2nd-time-index",
templateUrl: "./appraisal-2nd-time-index.component.html",
styleUrls: []
})
export class Appraisal2ndTimeIndexComponent extends BaseListComponent implements OnInit {
pageTitle = "รับชำระเงิน/ออกใบเสร็จรับเงิน";
action = "pending";
apiUrl: string = API.quotation;
api: any = API;
displayedColumns: string[] = [];
masterProductCategory: any = [];
masterProductBrand: any = [];
filterKeyword: Subject<string> = new Subject<string>();
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService,
public activatedRoute: ActivatedRoute,
public changeDetectorRef: ChangeDetectorRef
) {
super();
this.filterKeyword.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(model => {
this.getData();
});
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.action = params["action"];
this.dataFilter.step = 3;
this.dataFilter.status2ndTime = this.action;
if (!this.action) this.router.navigate(["/pages/appraisal/2nd-time/list", EStatusQuotation.WAIT]);
await this.getData();
});
}
async onTabs(action?: any) {
this.dataFilter = {};
return this.router.navigate(["/pages/appraisal/2nd-time/list", action]);
}
onAction(id?: any) {
if (id) return this.router.navigate([`/pages/appraisal/2nd-time/do/${this.action}`, id]);
return;
}
async getData($event?: any) {
try {
this.dataFilter.keywordColumn = "productNo,productName,quotationNo,price,deposit,sellerDeposit2ndTime";
this.dataSource = [];
let url = API.quotation;
if (this.action === EStatusQuotation.WAIT) {
url = API.quotation;
this.displayedColumns = ["action", "price", "deposit", "customerFirstName", "productNo", "productName", "productBrandName", "productSize", "productWeight", "productColor", "productYear"];
}
if (this.action === EStatusQuotation.EVALUATED) {
url = API.quotation;
delete this.dataFilter.step;
this.displayedColumns = ["price", "deposit", "sellerDeposit2ndTime", "quotationNo", "customerFirstName", "productNo", "productName", "status"];
}
const dataSource = await lastValueFrom(this.appService.get(this.setParams(url, $event)));
this.dataSource = this.setDataSource<any>(dataSource);
} catch (e) {
this.dataSource = [];
}
}
onFilter($event?: any) {
this.filterKeyword.next($event);
}
clearDate($event?: any) {
$event.stopPropagation();
this.dataFilter.createdDate = null;
}
async onSort($event: any) {
this.dataFilter.orderBy = $event.active;
this.dataFilter.sort = $event.direction;
await this.getData();
console.log($event);
}
async onDelete(ids: any) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
try {
await lastValueFrom(this.appService.delete(this.apiUrl, ids));
await this.appService.message(EAction.SUCCESS, EText.DELETE);
await this.getData(this.getCurrentPage());
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
onExport() {
const filter = generateParamsValue(this.dataFilter);
const url = `${API.quotation}/export-2nd-time?${filter ? '&' + filter : '' }`;
window.open(url);
}
}

View File

@@ -0,0 +1,3 @@
<mat-progress-bar *ngIf="!pdfView" mode="indeterminate"></mat-progress-bar>
<iframe *ngIf="pdfView" [src]="pdfView"></iframe>

View File

@@ -0,0 +1,94 @@
import { Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { lastValueFrom } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API } from "../../../../@config/app";
import { Router } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
@Component({
selector: "app-appraisal-1st-time-index",
templateUrl: "./appraisal-2nd-time-pdf.component.html",
styleUrls: []
})
export class Appraisal2ndTimePdfComponent extends BaseListComponent implements OnInit {
pageTitle = "ใบเสนอราคา";
apiUrl: string = API.quotation;
api: any = API;
dataView: any;
pdfView: any;
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService,
private sanitizer: DomSanitizer
) {
super();
}
async ngOnInit() {
await this.getData();
}
onAction(id?: any) {
if (id) return this.router.navigate(["/pages/appraisal/1st-time/do", "update", id]);
return this.router.navigate(["/pages/appraisal/1st-time/do", "create"]);
}
async getData($event?: any) {
try {
const data = {
doc_no: "string",
product_code: "string",
customer_name: "string",
phone_no: "string",
installment_start_date: "string",
picture: "string",
price: 0,
seller_deposit: 0,
cmfs_deposit: 0,
total_balance: 0,
installment: 0,
packing: 0,
luxury_handbag_authentication: 0,
bankfee_insurance_storage: 0,
transfer_amount: 0,
data: [
{
due_date: "string",
principle: 0,
interest_total: 0,
bank_fee: 0,
total_payment: 0,
principle_total: 0
}
],
total1: 0,
total2: 0,
total3: 0,
total4: 0
}
this.dataView = await lastValueFrom(this.appService.post(`${this.api.quotationReport}/pdf`, data, { responseType: 'arraybuffer' }));
const url = URL.createObjectURL(new Blob([this.dataView], { type: 'application/pdf' }));
this.pdfView = this.sanitizer.bypassSecurityTrustResourceUrl(url);
} catch (e) {
console.log(e);
}
}
}

View File

@@ -0,0 +1,30 @@
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {Appraisal3rdTimeIndexComponent} from './index/appraisal-3rd-time-index.component';
import {Appraisal3rdTimeDoComponent} from "./do/appraisal-3rd-time-do.component";
import { Appraisal3rdTimeHistoryComponent } from "./history/appraisal-3rd-time-history.component";
import { Appraisal3rdTimePdfComponent } from "./pdf/appraisal-3rd-time-pdf.component";
const routes: Routes = [
{path: '', component: Appraisal3rdTimeIndexComponent},
{path: 'list', component: Appraisal3rdTimeIndexComponent},
{path: 'list/:action', component: Appraisal3rdTimeIndexComponent},
{path: 'do/:action', component: Appraisal3rdTimeDoComponent},
{path: 'do/:action/:id', component: Appraisal3rdTimeDoComponent},
{path: 'history', component: Appraisal3rdTimeHistoryComponent},
{path: 'history/pdf/:id', component: Appraisal3rdTimePdfComponent},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RoutingModule {
}
export const RoutingComponents = [
Appraisal3rdTimeIndexComponent,
Appraisal3rdTimeDoComponent,
Appraisal3rdTimeHistoryComponent,
Appraisal3rdTimePdfComponent,
];

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import {RoutingComponents, RoutingModule} from './appraisal-3rd-time-routing.module';
import {AppSharedModule} from "../../../app.shared";
import { NgOptimizedImage } from "@angular/common";
@NgModule({
declarations: [
...RoutingComponents,
],
imports: [
AppSharedModule,
RoutingModule,
NgOptimizedImage
]
})
export class Appraisal3rdTimeModule {}

View File

@@ -0,0 +1,401 @@
<form class="main-form" #ngf="ngForm" (ngSubmit)="onSubmit(ngf)">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลลูกค้า</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 ">
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>ค้นหาเลขบัตรประชาชน</mat-label>-->
<!-- <ng-select placeholder="ค้นหาเลขบัตรประชาชน" name="filterIdCard" #filterIdCard="ngModel" [(ngModel)]="dataFilter.idCard" (ngModelChange)="onChangeFilter($event)" appendTo="body" >-->
<!-- <ng-option *ngFor="let item of customer" [value]="item.idCard">{{item.idCard}} : {{item.prefix}} {{item.firstName}} {{item.lastName}}</ng-option>-->
<!-- </ng-select>-->
<!-- </div>-->
<!-- <div class="col-span-8 md:hidden"></div>-->
<div class="col-span-2 md:col-span-12 ">
<mat-label>คำนำหน้า</mat-label>
<ng-select placeholder="เลือกคำนำหน้า" name="prefix" #prefix="ngModel" [(ngModel)]="dataForm.customer.prefix" appendTo="body" required>
<ng-option *ngFor="let item of prefixData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อลูกค้า</mat-label>
<mat-form-field>
<input matInput name="firstName" #firstName="ngModel" [(ngModel)]="dataForm.customer.firstName" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>นามสกุล</mat-label>
<mat-form-field>
<input matInput name="lastName" #lastName="ngModel" [(ngModel)]="dataForm.customer.lastName" >
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>เพศ</mat-label>
<ng-select placeholder="เลือกเพศ" name="gender" #gender="ngModel" [(ngModel)]="dataForm.customer.gender" appendTo="body" >
<ng-option *ngFor="let item of genderData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เบอร์โทร</mat-label>
<mat-form-field>
<input matInput name="phone" #phone="ngModel" [(ngModel)]="dataForm.customer.phone" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เลขบัตรประชาชน</mat-label>
<mat-form-field>
<input matInput name="idCard" #idCard="ngModel" [(ngModel)]="dataForm.customer.idCard" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<div style="height: 20px;"></div>
<div class="flex items-center">
<div class="">
<input hidden type="file" accept="image/*" #idCardImages (change)="onAttachmentsIdCard($event, 'idcard')" />
<button type="button" class="btn btn-icon-upload color-main" (click)="idCardImages.click()">
<ng-container *ngIf="!dataForm.customer.idCardImage"> <i class="bi bi-plus-circle "></i> สำเนาบัตรประชาชน</ng-container>
<ng-container *ngIf="dataForm.customer.idCardImage"> <i class="bi bi-pencil" matTooltip="แก้ไขสำเนาบัตรประชาชน"></i> </ng-container>
</button>
</div>
<div style="padding-top: 4px;">
<ng-container *ngIf="dataForm.customer.idCardImage">
<div class="cursor-pointer" (click)="onAttachmentsView('images')"><i class="bi bi-search"></i> ดูสำเนาบัตรประชาชน</div>
</ng-container>
</div>
</div>
</div>
<div class="col-span-8 md:hidden"></div>
<div class="col-span-12 md:col-span-12 ">
<mat-label>ที่อยู่ตามบัตรประชาชน</mat-label>
<mat-form-field>
<input matInput name="address" #address="ngModel" [(ngModel)]="dataForm.customer.address" (ngModelChange)="onChangeAddress('address', $event)">
</mat-form-field>
</div>
<div class="col-span-12 md:col-span-12 ">
<mat-label>ที่อยู่ในการจัดส่ง</mat-label>
<label class="inline-flex items-center cursor-pointer select-none ml-2">
<input type="checkbox" name="isAddress" [(ngModel)]="dataForm.customer.isAddress" (ngModelChange)="onChangeAddress('isAddress', $event)">
<span style="padding-left: 2px;">ใช้ที่อยู่ตามบัตรประชาชน</span>
</label>
<mat-form-field>
<input matInput name="deliveryAddress" #deliveryAddress="ngModel" [(ngModel)]="dataForm.customer.deliveryAddress" [disabled]="dataForm.customer.isAddress">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>E-mail</mat-label>
<mat-form-field>
<input matInput name="email" #email="ngModel" [(ngModel)]="dataForm.customer.email">
</mat-form-field>
</div>
<div class="col-span-8 md:col-span-12 ">
<mat-label>อาชีพ</mat-label>
<mat-form-field>
<input matInput name="occupation" #occupation="ngModel" [(ngModel)]="dataForm.customer.occupation">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID Line</mat-label>
<mat-form-field>
<input matInput name="line" #line="ngModel" [(ngModel)]="dataForm.customer.line">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>Facebook</mat-label>
<mat-form-field>
<input matInput name="facebook" #facebook="ngModel" [(ngModel)]="dataForm.customer.facebook">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>IG</mat-label>
<mat-form-field>
<input matInput name="ig" #ig="ngModel" [(ngModel)]="dataForm.customer.ig">
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลคนขาย</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-4 md:col-span-12 ">
<mat-label>ค้นหาเลขประจําตัวผู้เสียภาษี</mat-label>
<ng-select placeholder="ค้นหาเลขประจําตัวผู้เสียภาษี" name="filterSeller" #filterSeller="ngModel" [(ngModel)]="dataFilter.filterSeller" (ngModelChange)="onChangeFilter($event)" appendTo="body" >
<ng-option *ngFor="let item of seller" [value]="item.id"> {{item.idCard}} : {{item.prefix}} {{item.firstName}} {{item.lastName}}</ng-option>
</ng-select>
</div>
<div class="col-span-8 md:hidden"></div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>คำนำหน้า</mat-label>
<ng-select placeholder="เลือกคำนำหน้า" name="sellerprefix" #sellerprefix="ngModel" [(ngModel)]="dataForm.seller.prefix" appendTo="body" required>
<ng-option *ngFor="let item of prefixData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อลูกค้า</mat-label>
<mat-form-field>
<input matInput name="sellerfirstName" #sellerfirstName="ngModel" [(ngModel)]="dataForm.seller.firstName" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>นามสกุล</mat-label>
<mat-form-field>
<input matInput name="sellerlastName" #sellerlastName="ngModel" [(ngModel)]="dataForm.seller.lastName" >
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>เพศ</mat-label>
<ng-select placeholder="เลือกเพศ" name="sellergender" #sellergender="ngModel" [(ngModel)]="dataForm.seller.gender" appendTo="body" >
<ng-option *ngFor="let item of genderData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เบอร์โทร</mat-label>
<mat-form-field>
<input matInput name="sellerPhone" #sellerPhone="ngModel" [(ngModel)]="dataForm.seller.phone" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>Facebook</mat-label>
<mat-form-field>
<input matInput name="sellerFacebook" #sellerFacebook="ngModel" [(ngModel)]="dataForm.seller.facebook">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID Line</mat-label>
<mat-form-field>
<input matInput name="sellerLine" #sellerLine="ngModel" [(ngModel)]="dataForm.seller.line">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID LINE ร้านค้า</mat-label>
<mat-form-field>
<input matInput name="sellerLineShop" #sellerLineShop="ngModel" [(ngModel)]="dataForm.seller.lineShop">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>IG</mat-label>
<mat-form-field>
<input matInput name="sellerIg" #sellerIg="ngModel" [(ngModel)]="dataForm.seller.ig">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>S/N สินค้า</mat-label>
<mat-form-field>
<input matInput name="sellerSnProduct" #sellerSnProduct="ngModel" [(ngModel)]="dataForm.seller.snProduct">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>แหล่งที่มา</mat-label>
<ng-select placeholder="เลือกแหล่งที่มา" name="source" #source="ngModel" [(ngModel)]="dataForm.source" appendTo="body" >
<ng-option *ngFor="let item of sourceData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>ค่าขนส่ง</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="shippingCost" #shippingCost="ngModel" [(ngModel)]="dataForm.shippingCost">-->
<!-- </mat-form-field>-->
<!-- </div>-->
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mt-6">
<div class="card-header ">
<div class="grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-6 md:col-span-12">
<div class="flex items-center w-full">
<div class="">รูปแบบการวัด</div>
</div>
</div>
<div class="col-span-6 md:col-span-12 ">
</div>
</div>
</div>
<div class="card-body">
<ng-container *ngFor="let item of dataForm.productMeasurement; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-7 md:col-span-11">
<label *ngIf="i === 0">รายการวัด</label>
<mat-form-field>
<input matInput name="productMeasurementName-{{i}}" #productMeasurementName="ngModel" [(ngModel)]="item.name" >
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-6">
<label *ngIf="i === 0">ขนาด</label>
<mat-form-field>
<input appNumberOnly matInput name="productMeasurementSize-{{i}}" #productMeasurementSize="ngModel" [(ngModel)]="item.size" >
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-5">
<label *ngIf="i === 0">หน่วยนับ</label>
<ng-select placeholder="เลือกหน่วยนับ" name="productMeasurementUnit-{{i}}" #productMeasurementUnit="ngModel" [(ngModel)]="item.unitId" appendTo="body" >
<ng-option *ngFor="let item of masterProductUnit" [value]="item.id">{{item.name}}</ng-option>
</ng-select>
</div>
</div>
</div>
</ng-container>
</div>
</div>
<div class="text-right mt-4 ">
<mat-checkbox name="depositChecked" #depositChecked="ngModel" [(ngModel)]="dataForm.depositChecked">ชำระเงินเพิ่ม</mat-checkbox>
</div>
<div class="card card-table mb-6">
<div class="card-body">
<div class="table-wrap">
<table class="tables ">
<thead>
<tr>
<th>BOM</th>
<th>Brand</th>
<th>Model</th>
<th>ราคาสินค้า</th>
<th>จำนวนเงินมัดจำ</th>
<th>ค่ามัดจำเพิ่ม</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center">{{dataForm.productNo}}</td>
<td class="text-center">{{dataForm.productBrandName}}</td>
<td class="text-center">{{dataForm.productName }}</td>
<td class="text-center">{{dataForm.price | number : '1.2-2'}}</td>
<td class="text-center">
<div class="b-color-orange">{{dataForm.deposit | number : '1.2-2'}}</div>
</td>
<td>
<mat-form-field>
<input matInput name="paymentAmount" #paymentAmount="ngModel" [(ngModel)]="dataForm.paymentAmount" [required]="dataForm.depositChecked" [disabled]="!dataForm.depositChecked">
</mat-form-field>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">รูปสินค้าจากลูกค้า</div>
<div class="ml-4">
<input hidden type="file" accept="image/*" #productImages (change)="onAttachments($event, 'products')" />
<button type="button" class="btn btn-sm btn-success-o" (click)="productImages.click()">เพิ่มรูปภาพสินค้า</button>
</div>
</div>
</div>
<div class="card-body">
<div class="list-images">
<div class=" grid grid-cols-12 gap-2 md:gap-2 items-center">
<ng-container *ngFor="let item of attachments; let i = index">
<div class="col-span-2 md:col-span-4">
<div class="flex justify-center items-center list-images-item">
<div class="list-images-action">
<i *ngIf="dataForm.coverImage !== item" (click)="dataForm.coverImage = item"
matTooltip="ใช้ทำเอกสาร" class="bi bi-star color-main cursor-pointer select-none"></i>
<i *ngIf="dataForm.coverImage === item" class="bi bi bi-star-fill color-main cursor-pointer select-none"></i>
<i (click)="onRemoveAttachments(i, item)" class="bi bi-x-circle color-red cursor-pointer select-none"></i>
</div>
<img src="{{storage.products}}/{{item}}" alt="">
</div>
</div>
</ng-container>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">อุปกรณ์</div>
</div>
</div>
<div class="card-body">
<ng-container *ngFor="let item of equipmentData; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-1 md:col-span-2 text-right">
<div *ngIf="i === 0" style="height: 25px;"></div>
<input type="checkbox" name="isCheck-{{i}}" #isCheck="ngModel" [(ngModel)]="item.isCheck">
</div>
<div class="col-span-9 md:col-span-8 cursor-pointer select-none" (click)="item.isCheck = !item.isCheck">
<div *ngIf="i === 0" style="height: 25px;"></div>
{{item.name}}
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="isEquipmentOther()">
<ng-container *ngFor="let item of equipmentOtherData; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-1 md:col-span-2 text-right">
</div>
<div class="col-span-9 md:col-span-8 cursor-pointer select-none" >
<mat-form-field>
<input matInput name="equipmentOtherItem-{{i}}" #equipmentOtherItem="ngModel" [(ngModel)]="item.value" >
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-2 " >
<i (click)="onAddEquipmentOther()" class="bi bi-plus-circle color-green cursor-pointer select-none mr-2"></i>
<i (click)="onRemoveEquipmentOther(i)" class="bi bi-x-circle color-red cursor-pointer select-none"></i>
</div>
</div>
</div>
</ng-container>
</ng-container>
<div style="height: 40px;"></div>
</div>
</div>
<div class="main-form-action text-right" *ngIf="dataForm.status3rdTime !== 'complete' ">
<button type="submit" class="btn btn-submit">บันทึก</button>
<button type="button" class="btn btn-back" (click)="onAction('back')">ยกเลิก</button>
</div>
</form>

View File

@@ -0,0 +1,283 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import {API, EAction, EText, GENDER, PREFIX, SOURCES, STORAGE} from "../../../../@config/app";
import { AppService } from "../../../../app.service";
import { lastValueFrom } from "rxjs";
import { BaseFormComponent } from "../../../../@common/base/base-form.component";
import { ActivatedRoute, Router } from "@angular/router";
import { IProduct } from "../../../../app.interface";
import deepCopy from "../../../../@common/utils/DeepCopy";
import { AttachmentsViewComponent } from "../../../@popup/attachments-view/attachments-view.component";
import { MatDialog } from "@angular/material/dialog";
@Component({
selector: "app-appraisal-3rd-time-do",
templateUrl: "./appraisal-3rd-time-do.component.html",
styleUrls: []
})
export class Appraisal3rdTimeDoComponent extends BaseFormComponent implements OnInit {
override dataForm: any = {};
dataView: IProduct = {};
auth: any = {};
title = "";
api: any = API;
storage: any = STORAGE;
attachments: any = [];
settings: any = [];
masterProductUnit: any = [];
deviation: any = 0;
prefixData = PREFIX;
genderData = GENDER;
sourceData = SOURCES;
customer : any = [];
seller : any = [];
equipmentData : any = [];
equipmentOtherData : any = [{value : ''}];
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
public changeDetectorRef: ChangeDetectorRef,
public appService: AppService,
private attachmentsView: MatDialog,
) {
super();
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.ids = params["id"];
this.action = params["action"];
this.auth = this.appService.auth();
this.dataForm.customer = {};
this.dataForm.seller = {};
this.defaultEquipmentData();
this.settings = await lastValueFrom(this.appService.get(`${this.api.settings}/getByCode/DEVIATION`));
this.masterProductUnit = await lastValueFrom(this.appService.get(`${this.api.masterProductUnit}?showAll=true&status=true&orderBy=name&sort=asc`));
this.customer = await lastValueFrom(this.appService.get(`${API.customer}?showAll=true`));
this.seller = await lastValueFrom(this.appService.get(`${API.customer}?showAll=true`));
if (this.ids) await this.getData();
});
}
async onAction(action: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.BACK));
if (!sweetalert.isConfirmed) return;
if (action === "back") return this.router.navigate(["/pages/appraisal/3rd-time/list", this.action]);
return;
}
async getData() {
if (!this.ids) this.appService.message(EAction.INFO, EText.NO_DATA);
try {
this.dataForm = await lastValueFrom(this.appService.get(`${this.api.quotation}/getById/${this.ids}`));
this.attachments = this.dataForm.images ? this.dataForm.images?.split(",") : [];
this.dataForm.productMeasurement.map((item : any) => {
item.sizeOld = item.size;
item.size = this.dataForm.status3rdTime === 'wait' ? null : item.size;
})
this.dataForm.deposit = Number(this.dataForm.deposit) + Number(this.dataForm.sellerDeposit2ndTime)
if (this.dataForm.customer.isAddress) this.dataForm.customer.deliveryAddress = this.dataForm.customer.address;
if(!this.dataForm.sellerId) {
this.dataForm.seller = {};
this.dataForm.seller.name = this.dataForm.sellerName;
this.dataForm.seller.phone = this.dataForm.sellerPhone;
this.dataForm.seller.facebook = this.dataForm.sellerFacebook;
this.dataForm.seller.line = this.dataForm.sellerLine;
this.dataForm.seller.lineShop = this.dataForm.sellerLineShop;
this.dataForm.seller.ig = this.dataForm.sellerIg;
this.dataForm.seller.snProduct = this.dataForm.sellerSnProduct;
}
const equipment : any[] = this.dataForm.equipment ? this.dataForm.equipment?.split(",") : [];
const equipmentOther : any[] = this.dataForm.equipmentOther ? this.dataForm.equipmentOther?.split(",") : [];
if (equipment.length) {
this.equipmentData.map((d: any) => {
if (equipment.includes(d.name)) d.isCheck = true;
})
}
if (equipmentOther.length) {
this.equipmentOtherData = [];
equipmentOther.map((d : any) => {
this.equipmentOtherData.push({ value : d});
})
}
this.changeDetectorRef.detectChanges();
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onChangeFilter(value : any) {
if (!value) return await this.getData();
this.dataForm.seller = this.seller.filter((f : any) => f.id === value)?.[0];
}
onChangeAddress(key : string, value : any) {
if (key === 'isAddress') this.dataForm.customer.deliveryAddress = value ? this.dataForm.customer.address : '';
if (key === 'address') this.dataForm.customer.deliveryAddress = this.dataForm.customer.isAddress ? value : this.dataForm.customer.deliveryAddress;
}
async onSubmit(form: any) {
if (!form.valid) return false;
const error : any[] = [];
this.dataForm.productMeasurement.map((item : any) => {
if (!item.size) return;
const deviation = (Number(this.settings.value) / 100) * Number(item.sizeOld);
const deviationSize = Number(item.sizeOld) + deviation;
console.log(deviationSize);
if (Number(item.size) > deviationSize) {
const msg = `${item.name} ขนาดมากกว่าค่าเบี่ยงเบนการวัด (${deviationSize})`;
error.push(msg)
}
})
if (error.length > 0) {
const msg = error.join("<br>");
return this.appService.html(EAction.INFO, msg);
}
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.CREATE));
if (!sweetalert.isConfirmed) return;
const equipment : any[] = [];
this.equipmentData.map((d : any) => {
if (d.isCheck) equipment.push(d.name);
})
this.dataForm.equipment = equipment?.[0] ? equipment.join(",") : null;
this.dataForm.equipmentOther = null;
if (equipment.includes('อื่นๆ')) {
const equipmentOther : any[] = [];
this.equipmentOtherData.map((d : any) => {
equipmentOther.push(d.value);
})
this.dataForm.equipmentOther = equipmentOther?.[0] ? equipmentOther.join(",") : null;
}
this.dataForm.pageAction = '3rd-time';
this.dataForm.images = this.attachments?.[0] ? this.attachments.join(",") : null;
return await this.onUpdate();
}
async onUpdate() {
try {
await lastValueFrom(this.appService.post(`${this.api.quotation}/update/${this.ids}`, this.dataForm));
await this.appService.message(EAction.SUCCESS, EText.UPDATE);
await this.router.navigate(["/pages/appraisal/3rd-time/list", this.action]);
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onAttachments($event: any, type: any) {
const file = $event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("ref", type);
formData.append("file", file);
try {
const res = await lastValueFrom(this.appService.post(`${this.api.attachments}/products`, formData));
if (!this.attachments[0]) {
this.dataForm.coverImage = res.fileName;
}
this.attachments.push(res.fileName);
console.log(this.attachments, res);
this.changeDetectorRef.detectChanges();
} catch (e) {
this.appService.message(EText.ERROR);
}
}
async onRemoveAttachments(i: number, fileName: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
// await lastValueFrom(this.appService.delete(`${this.api.attachments}/deleteByName`, fileName));
this.attachments?.splice(i, 1);
if (!this.attachments[0]) {
this.dataForm.coverImage = null;
}
this.changeDetectorRef.detectChanges();
}
async onAttachmentsIdCard($event: any, type: any) {
const file = $event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("ref", type);
formData.append("file", file);
try {
const res = await lastValueFrom(this.appService.post(`${API.attachments}/images`, formData));
this.dataForm.customer.idCardImage = res.fileName;
this.changeDetectorRef.detectChanges();
} catch (e) {
this.appService.message(EText.ERROR);
}
}
async onAttachmentsView(type : any) {
const dialogConfig = deepCopy(this.dialogConfig);
dialogConfig.data.action = EAction.POPUP;
dialogConfig.data.title = 'สำเนาบัตรประชาชน';
dialogConfig.data.type = type;
dialogConfig.data.images = this.dataForm.customer.idCardImage;
const dialogRef = this.attachmentsView.open(AttachmentsViewComponent, dialogConfig);
const afterClosed = await lastValueFrom(dialogRef.afterClosed());
}
defaultEquipmentData() {
this.equipmentData = [
{ name : 'ตัวเปล่า', isCheck : false},
{ name : 'ถุงกระดาษ', isCheck : false},
{ name : 'ถุงผ้า', isCheck : false},
{ name : 'ใบเสร็จ', isCheck : false},
{ name : 'บุ๊ค 1', isCheck : false},
{ name : 'บุ๊ค 2', isCheck : false},
{ name : 'ดอกคามิเลีย', isCheck : false},
{ name : 'ริบบิ้น', isCheck : false},
{ name : 'ถุงกันฝน', isCheck : false},
{ name : 'การ์ด', isCheck : false},
{ name : 'สายกระเป๋า', isCheck : false},
{ name : 'ใบเซอร์', isCheck : false},
{ name : 'พวงกุญแจแม่ และลูก', isCheck : false},
{ name : 'กล่อง', isCheck : false},
{ name : 'สายนาฬิกา', isCheck : false},
{ name : 'ใบรับประกัน', isCheck : false},
{ name : 'ใบตรวจ', isCheck : false},
{ name : 'อื่นๆ', isCheck : false},
]
}
isEquipmentOther() {
return this.equipmentData.find((f : any) => f.name === 'อื่นๆ' && f.isCheck === true)
}
onAddEquipmentOther() {
this.equipmentOtherData.push({value : ''});
}
onRemoveEquipmentOther(i : number) {
if (i === 0) return;
this.equipmentOtherData?.splice(i, 1);
this.changeDetectorRef.detectChanges();
}
}

View File

@@ -0,0 +1,118 @@
<div class="card card-table">
<div class="card-filter text-right">
<div class="card-filter-section grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-5 md:col-span-12 md:order-2">
<mat-form-field>
<i matTextPrefix class="bi bi-search"></i>
<input matInput name="keyword" #keyword="ngModel" [(ngModel)]="dataFilter.keyword" (ngModelChange)="onFilter($event)">
</mat-form-field>
</div>
</div>
<div class="card-filter-section grid grid-cols-12 gap-4 items-center md:gap-2 ">
<div class="col-span-3 md:col-span-6">
<mat-form-field>
<input
matInput
name="startDate"
#startDate="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataFilter.createdDate"
[matDatepicker]="dpkName"
readonly
(ngModelChange)="getData()"
/>
<!-- <mat-icon matSuffix (click)="clearDate($event)">clear</mat-icon>-->
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-9 md:col-span-12 ">
<div class="flex w-full ">
<div class="">จำนวนทั้งหมด {{totalItem}} รายการ</div>
<div class="pl-2 pr-2">|</div>
<div class="">ค้นหาจำนวน {{totalOfElement}} รายการ</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-wrap">
<table class="table table-main" mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<ng-container matColumnDef="quotationNo">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>เลขที่ใบเสนอราคา</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.quotationNo}}</td>
</ng-container>
<ng-container matColumnDef="customerFirstName">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>ชื่อลูกค้า</th>
<td mat-cell *matCellDef="let item" width="150" class="">
<ng-container *ngIf="item.customerId"> {{item.customer?.prefix}}{{item.customer?.firstName}} {{item.customer?.lastName}}</ng-container>
<ng-container *ngIf="!item.customerId">{{item.customerFirstName}} {{item.customerLastName}}</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="productNo">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>BOM</th>
<td mat-cell *matCellDef="let item" width="150" class="tac">{{item.productNo}}</td>
</ng-container>
<ng-container matColumnDef="productName">
<th mat-header-cell *matHeaderCellDef class="tal">Model</th>
<td mat-cell *matCellDef="let item" class="" style="min-width: 200px;">{{item.productName }}</td>
</ng-container>
<ng-container matColumnDef="price">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-green"> {{item.price | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="wantToInstallmentTerm">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ระยะเวลาผ่อน</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.wantToInstallmentTerm }} งวด</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่บันทึก</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.createdDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef class="tac">สถานะ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.status === 'paid' " class="status status-active">ชำระแล้ว</div>
<div *ngIf="item.status === 'pending'" class="status status-disabled">รอชำระ</div>
</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef width="80">Action</th>
<td mat-cell *matCellDef="let item">
<div class="action flex justify-center">
<div class="item">
<i class="bi bi-filetype-pdf color-main" (click)="onAction(item.id)"></i>
</div>
<div class="item" *ngIf="item.status === 'pending'">
<i class="bi bi-trash3 color-red" (click)="onDelete(item.id)"></i>
</div>
</div>
</td>
</ng-container>
</table>
<div *ngIf="dataSourceCount === 0" class="no-data"></div>
</div>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons (page)="getData($event)"></mat-paginator>
</div>
</div>

View File

@@ -0,0 +1,86 @@
import { Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API, EAction, EText } from "../../../../@config/app";
import { Router } from "@angular/router";
@Component({
selector: "app-appraisal-1st-time-index",
templateUrl: "./appraisal-3rd-time-history.component.html",
styleUrls: []
})
export class Appraisal3rdTimeHistoryComponent extends BaseListComponent implements OnInit {
pageTitle = "ประวัติการสร้างใบเสนอราคา";
apiUrl: string = API.quotation;
api: any = API;
displayedColumns: string[] = ["action", "quotationNo", "customerFirstName", "productNo", "productName", "price", "wantToInstallmentTerm", "createdDate", "status"];
masterProductCategory: any = [];
masterProductBrand: any = [];
filterKeyword: Subject<string> = new Subject<string>();
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService
) {
super();
this.filterKeyword.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(model => {
this.getData();
});
}
async ngOnInit() {
await this.getData();
}
onAction(id?: any) {
if (id) return this.router.navigate(["/pages/appraisal/1st-time/history/pdf", id]);
return this.router.navigate(["/pages/appraisal/1st-time/do", "create"]);
}
async getData($event?: any) {
try {
this.dataFilter.keywordColumn = "quotationNo,productNo,customerFirstName,customerLastName";
const dataSource = await lastValueFrom(this.appService.get(this.setParams(this.apiUrl, $event)));
this.dataSource = this.setDataSource<any>(dataSource);
} catch (e) {
this.dataSource = [];
}
}
onFilter($event?: any) {
this.filterKeyword.next($event);
}
clearDate($event?: any) {
$event.stopPropagation();
this.dataFilter.createdDate = null;
}
async onSort($event: any) {
this.dataFilter.orderBy = $event.active;
this.dataFilter.sort = $event.direction;
await this.getData();
console.log($event);
}
async onDelete(ids: any) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
try {
await lastValueFrom(this.appService.delete(this.apiUrl, ids));
await this.appService.message(EAction.SUCCESS, EText.DELETE);
await this.getData(this.getCurrentPage());
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
}

View File

@@ -0,0 +1,187 @@
<div class="card card-table">
<div class="card-filter text-right">
<div class="card-filter-section grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-5 md:col-span-12 md:order-2">
<mat-form-field>
<i matTextPrefix class="bi bi-search"></i>
<input matInput name="keyword" #keyword="ngModel" [(ngModel)]="dataFilter.keyword" (ngModelChange)="onFilter($event)">
</mat-form-field>
</div>
<div class="col-span-7 md:col-span-12 md:order-1">
<button type="button" class="btn btn-export" (click)="onExport()">Export</button>
</div>
</div>
<div class="card-filter-section grid grid-cols-12 gap-4 items-center md:gap-2 ">
<div class="col-span-6 md:col-span-12">
<div class="tabs-btn">
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'wait'}" (click)="onTabs('wait')">รอประเมิน</button>
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'evaluated'}" (click)="onTabs('evaluated')">ประเมินแล้ว</button>
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'complete'}" (click)="onTabs('complete')">ข้อมูลครบถ้วน</button>
</div>
</div>
<div class="col-span-6 md:col-span-12 ">
<div class="flex w-full justify-end md:justify-start">
<div class="">จำนวนทั้งหมด {{totalItem}} รายการ</div>
<div class="pl-2 pr-2">|</div>
<div class="">ค้นหาจำนวน {{totalOfElement}} รายการ</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-wrap">
<table class="table table-main" mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<ng-container matColumnDef="quotationNo">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>เลขที่ใบเสนอราคา</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.quotationNo}}</td>
</ng-container>
<ng-container matColumnDef="customerFirstName">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>ชื่อลูกค้า</th>
<td mat-cell *matCellDef="let item" width="150" class="">
<ng-container *ngIf="item.customerId"> {{item.customer?.prefix}}{{item.customer?.firstName}} {{item.customer?.lastName}}</ng-container>
<ng-container *ngIf="!item.customerId">{{item.customerFirstName}} {{item.customerLastName}}</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="productNo">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>BOM</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.productNo}}</td>
</ng-container>
<ng-container matColumnDef="productName">
<th mat-header-cell *matHeaderCellDef class="tal">Model</th>
<td mat-cell *matCellDef="let item" class="" style="min-width: 220px;">{{item.productName }}</td>
</ng-container>
<ng-container matColumnDef="productBrandName">
<th mat-header-cell *matHeaderCellDef class="tal">Brand</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productBrandName }}</td>
</ng-container>
<ng-container matColumnDef="productSize">
<th mat-header-cell *matHeaderCellDef class="tal">Main</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productSize }}</td>
</ng-container>
<ng-container matColumnDef="productWeight">
<th mat-header-cell *matHeaderCellDef class="tal" width="150">น้ำหนัก</th>
<td mat-cell *matCellDef="let item" class="">{{item.productWeight }}</td>
</ng-container>
<ng-container matColumnDef="productColor">
<th mat-header-cell *matHeaderCellDef class="tal">Color</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productColor }}</td>
</ng-container>
<ng-container matColumnDef="productYear">
<th mat-header-cell *matHeaderCellDef class="tal">Year</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productYear }}</td>
</ng-container>
<ng-container matColumnDef="price">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-green"> {{item.price | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="deposit">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนมัดจำ</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-orange"> {{item.deposit | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="wantToInstallmentTerm">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ระยะเวลาผ่อน</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.wantToInstallmentTerm }} งวด</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่บันทึก</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.createdDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef class="tac">สถานะ</th>
<td mat-cell *matCellDef="let item" class="tac" style="min-width: 100px;">
<div *ngIf="item.status === 'paid' " class="status status-active">ชำระแล้ว</div>
<div *ngIf="item.status === 'pending'" class="status status-disabled">รอชำระ</div>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ประเภทการชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.type === 'deposit' " class="status-text status-deposit">ค่ามัดจำ</div>
<div *ngIf="item.type === 'installment'" class="status-text status-installment">ผ่อนสินค้า</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentType">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ประเภทการชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.paymentType === 'deposit' " class="status-text status-deposit">ค่ามัดจำ</div>
<div *ngIf="item.paymentType === 'installment'" class="status-text status-installment">ผ่อนสินค้า</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentMethod">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>วิธีชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.paymentMethod === 'transfer' " class="status-text status-transfer">โอนเงิน</div>
<div *ngIf="item.paymentMethod === 'cash'" class="status-text status-cash">เงินสด</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentAmountAll">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="tac">
<div class="b-color-green">{{item.paymentAmountAll | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="sellerDeposit3rdTime">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>เงินมัดจำเพิ่ม</th>
<td mat-cell *matCellDef="let item" class="tac">
<div class="b-color-red" *ngIf="item.sellerDeposit3rdTime">{{item.sellerDeposit3rdTime | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef width="80">Action</th>
<td mat-cell *matCellDef="let item">
<div class="action flex justify-center">
<div class="item" *ngIf="['wait','evaluated'].includes(item.status3rdTime)" >
<i class="bi bi-pencil-square icon-edit" (click)="onAction(item.id)"></i>
</div>
<div class="item" *ngIf="['complete'].includes(item.status3rdTime)" >
<i class="bi bi-eye color-green" (click)="onAction(item.id)"></i>
</div>
<!-- <div class="item" *ngIf="item.status === 'paid' ">-->
<!-- <i class="bi bi-filetype-pdf color-main" (click)="onAction(item.id)"></i>-->
<!-- </div>-->
</div>
</td>
</ng-container>
</table>
<div *ngIf="dataSource?.length === 0" class="no-data"></div>
</div>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons (page)="getData($event)"></mat-paginator>
</div>
</div>

View File

@@ -0,0 +1,132 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API, EAction, EStatusQuotation, EText } from "../../../../@config/app";
import { ActivatedRoute, Router } from "@angular/router";
import generateParamsValue from "../../../../@common/utils/GenerateParamsValue";
@Component({
selector: "app-appraisal-3rd-time-index",
templateUrl: "./appraisal-3rd-time-index.component.html",
styleUrls: []
})
export class Appraisal3rdTimeIndexComponent extends BaseListComponent implements OnInit {
pageTitle = "รับชำระเงิน/ออกใบเสร็จรับเงิน";
action = "pending";
apiUrl: string = API.quotation;
api: any = API;
displayedColumns: string[] = [];
masterProductCategory: any = [];
masterProductBrand: any = [];
filterKeyword: Subject<string> = new Subject<string>();
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService,
public activatedRoute: ActivatedRoute,
public changeDetectorRef: ChangeDetectorRef
) {
super();
this.filterKeyword.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(model => {
this.getData();
});
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.action = params["action"];
this.dataFilter.step = 4;
this.dataFilter.status3rdTime = this.action;
if (!this.action) this.router.navigate(["/pages/appraisal/3rd-time/list", EStatusQuotation.WAIT]);
await this.getData();
});
}
async onTabs(action?: any) {
this.dataFilter = {};
return this.router.navigate(["/pages/appraisal/3rd-time/list", action]);
}
onAction(id?: any) {
if (id) return this.router.navigate([`/pages/appraisal/3rd-time/do/${this.action}`, id]);
return;
}
async getData($event?: any) {
try {
this.dataFilter.keywordColumn = "productNo,productName,quotationNo,price,deposit,sellerDeposit3rdTime";
this.dataSource = [];
let url = API.quotation;
if (this.action === EStatusQuotation.WAIT) {
url = API.quotation;
this.displayedColumns = ["action", "price", "deposit", "customerFirstName", "productNo", "productName", "productBrandName", "productSize", "productWeight", "productColor", "productYear"];
}
if (this.action === EStatusQuotation.EVALUATED) {
url = API.quotation;
delete this.dataFilter.step;
this.displayedColumns = ["action", "price", "deposit", "sellerDeposit3rdTime", "quotationNo", "customerFirstName", "productNo", "productName", "status"];
}
if (this.action === EStatusQuotation.COMPLETE) {
url = API.quotation;
delete this.dataFilter.step;
this.displayedColumns = ["action", "price", "deposit", "sellerDeposit3rdTime", "quotationNo", "customerFirstName", "productNo", "productName", "status"];
}
const dataSource = await lastValueFrom(this.appService.get(this.setParams(url, $event)));
this.dataSource = this.setDataSource<any>(dataSource);
if (this.action === EStatusQuotation.WAIT) {
this.dataSource.map((item: any) => {
item.deposit = Number(item.deposit) + Number(item.sellerDeposit2ndTime)
})
}
} catch (e) {
this.dataSource = [];
}
}
onFilter($event?: any) {
this.filterKeyword.next($event);
}
clearDate($event?: any) {
$event.stopPropagation();
this.dataFilter.createdDate = null;
}
async onSort($event: any) {
this.dataFilter.orderBy = $event.active;
this.dataFilter.sort = $event.direction;
await this.getData();
console.log($event);
}
async onDelete(ids: any) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
try {
await lastValueFrom(this.appService.delete(this.apiUrl, ids));
await this.appService.message(EAction.SUCCESS, EText.DELETE);
await this.getData(this.getCurrentPage());
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
onExport() {
const filter = generateParamsValue(this.dataFilter);
const url = `${API.quotation}/export-3rd-time?${filter ? '&' + filter : '' }`;
window.open(url);
}
}

View File

@@ -0,0 +1,3 @@
<mat-progress-bar *ngIf="!pdfView" mode="indeterminate"></mat-progress-bar>
<iframe *ngIf="pdfView" [src]="pdfView"></iframe>

View File

@@ -0,0 +1,94 @@
import { Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { lastValueFrom } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API } from "../../../../@config/app";
import { Router } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
@Component({
selector: "app-appraisal-1st-time-index",
templateUrl: "./appraisal-3rd-time-pdf.component.html",
styleUrls: []
})
export class Appraisal3rdTimePdfComponent extends BaseListComponent implements OnInit {
pageTitle = "ใบเสนอราคา";
apiUrl: string = API.quotation;
api: any = API;
dataView: any;
pdfView: any;
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService,
private sanitizer: DomSanitizer
) {
super();
}
async ngOnInit() {
await this.getData();
}
onAction(id?: any) {
if (id) return this.router.navigate(["/pages/appraisal/1st-time/do", "update", id]);
return this.router.navigate(["/pages/appraisal/1st-time/do", "create"]);
}
async getData($event?: any) {
try {
const data = {
doc_no: "string",
product_code: "string",
customer_name: "string",
phone_no: "string",
installment_start_date: "string",
picture: "string",
price: 0,
seller_deposit: 0,
cmfs_deposit: 0,
total_balance: 0,
installment: 0,
packing: 0,
luxury_handbag_authentication: 0,
bankfee_insurance_storage: 0,
transfer_amount: 0,
data: [
{
due_date: "string",
principle: 0,
interest_total: 0,
bank_fee: 0,
total_payment: 0,
principle_total: 0
}
],
total1: 0,
total2: 0,
total3: 0,
total4: 0
}
this.dataView = await lastValueFrom(this.appService.post(`${this.api.quotationReport}/pdf`, data, { responseType: 'arraybuffer' }));
const url = URL.createObjectURL(new Blob([this.dataView], { type: 'application/pdf' }));
this.pdfView = this.sanitizer.bypassSecurityTrustResourceUrl(url);
} catch (e) {
console.log(e);
}
}
}

View File

@@ -0,0 +1,28 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { ContractApprovedIndexComponent } from "./index/contract-approved-index.component";
import { ContractApprovedDoComponent } from "./do/contract-approved-do.component";
import { ContractApprovedPdfComponent } from "./pdf/contract-approved-pdf.component";
const routes: Routes = [
{ path: "", component: ContractApprovedIndexComponent },
{ path: "list", component: ContractApprovedIndexComponent },
{ path: "list/:action", component: ContractApprovedIndexComponent },
{ path: "do/:action", component: ContractApprovedDoComponent },
{ path: "do/:action/:id", component: ContractApprovedDoComponent },
{ path: "pdf/:action/:id", component: ContractApprovedPdfComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RoutingModule {
}
export const RoutingComponents = [
ContractApprovedIndexComponent,
ContractApprovedDoComponent,
ContractApprovedPdfComponent
];

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import {RoutingComponents, RoutingModule} from './contract-approved-routing.module';
import {AppSharedModule} from "../../../app.shared";
import { NgOptimizedImage } from "@angular/common";
@NgModule({
declarations: [
...RoutingComponents,
],
imports: [
AppSharedModule,
RoutingModule,
NgOptimizedImage
]
})
export class ContractApprovedModule {}

View File

@@ -0,0 +1,694 @@
<ul class="progressbar">
<li [ngClass]="{ 'active' : isTabs === 1}" (click)="isTabs = 1">รายละเอียดหลัก</li>
<li [ngClass]="{ 'active' : isTabs === 2}" (click)="isTabs = 2">การจัดผ่อน</li>
<li [ngClass]="{ 'active' : isTabs === 3}" (click)="isTabs = 3">ข้อมูลรับชำระเงิน</li>
</ul>
<form class="main-form" #ngf="ngForm" (ngSubmit)="onSubmit(ngf)">
<ng-container *ngIf="isTabs === 1">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> สัญญาเงินกู้ระหว่าง CM-FS. Co., Ltd. ("บริษัทฯ") กับ</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-4 md:col-span-12 ">
<mat-label>เลขที่สัญญา/BOM</mat-label>
<mat-form-field>
<input matInput name="productNo" #productNo="ngModel" [(ngModel)]="dataForm.productNo" disabled >
</mat-form-field>
</div>
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>ชื่อลูกค้า</mat-label>-->
<!-- <mat-form-field>-->
<!-- <div class="flex">-->
<!-- <input style="width: 75px; padding-right: 0 !important;" matInput name="customerPrefix" #customerPrefix="ngModel" [(ngModel)]="dataForm.customer.prefix" disabled>-->
<!-- <input style="padding-left: 0 !important;" matInput name="customerFirstName" #customerFirstName="ngModel" [(ngModel)]="dataForm.customer.firstName" disabled>-->
<!-- </div>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>นามสกุล</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="customerLastName" #customerLastName="ngModel" [(ngModel)]="dataForm.customer.lastName" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>เบอร์โทร</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="customerPhone" #customerPhone="ngModel" [(ngModel)]="dataForm.customer.phone" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>เลขบัตรประชาชน</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="customerIdCard" #customerIdCard="ngModel" [(ngModel)]="dataForm.customer.idCard" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<div class="col-span-4 md:col-span-12 ">
<mat-label>วันที่ทำสัญญา</mat-label>
<mat-form-field>
<input
matInput
name="contractDate"
#contractDate="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataForm.contractDate"
[matDatepicker]="dpkName"
readonly
disabled
/>
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-4 md:hidden"></div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>คำนำหน้า</mat-label>
<ng-select placeholder="เลือกคำนำหน้า" name="prefix" #prefix="ngModel" [(ngModel)]="dataForm.customer.prefix" appendTo="body" disabled>
<ng-option *ngFor="let item of prefixData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อลูกค้า</mat-label>
<mat-form-field>
<input matInput name="firstName" #firstName="ngModel" [(ngModel)]="dataForm.customer.firstName" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>นามสกุล</mat-label>
<mat-form-field>
<input matInput name="lastName" #lastName="ngModel" [(ngModel)]="dataForm.customer.lastName" disabled>
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>เพศ</mat-label>
<ng-select placeholder="" name="gender" #gender="ngModel" [(ngModel)]="dataForm.customer.gender" appendTo="body" disabled>
<ng-option *ngFor="let item of genderData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เบอร์โทร</mat-label>
<mat-form-field>
<input matInput name="phone" #phone="ngModel" [(ngModel)]="dataForm.customer.phone" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เลขบัตรประชาชน</mat-label>
<mat-form-field>
<input matInput name="idCard" #idCard="ngModel" [(ngModel)]="dataForm.customer.idCard" disabled>
</mat-form-field>
</div>
<div class="col-span-12 md:col-span-12 ">
<mat-label>ที่อยู่ตามบัตรประชาชน</mat-label>
<mat-form-field>
<input matInput name="address" #address="ngModel" [(ngModel)]="dataForm.customer.address" disabled>
</mat-form-field>
</div>
<div class="col-span-12 md:col-span-12 ">
<mat-label>ที่อยู่ในการจัดส่ง</mat-label>
<label class="inline-flex items-center cursor-pointer select-none ml-2">
<input type="checkbox" name="isAddress" [(ngModel)]="dataForm.customer.isAddress" disabled>
<span style="padding-left: 2px;">ใช้ที่อยู่ตามบัตรประชาชน</span>
</label>
<mat-form-field>
<input matInput name="deliveryAddress" #deliveryAddress="ngModel" [(ngModel)]="dataForm.customer.deliveryAddress" [disabled]="dataForm.customer.isAddress" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>E-mail</mat-label>
<mat-form-field>
<input matInput name="email" #email="ngModel" [(ngModel)]="dataForm.customer.email" disabled>
</mat-form-field>
</div>
<div class="col-span-8 md:col-span-12 ">
<mat-label>อาชีพ</mat-label>
<mat-form-field>
<input matInput name="occupation" #occupation="ngModel" [(ngModel)]="dataForm.customer.occupation" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID Line</mat-label>
<mat-form-field>
<input matInput name="line" #line="ngModel" [(ngModel)]="dataForm.customer.line" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>Facebook</mat-label>
<mat-form-field>
<input matInput name="facebook" #facebook="ngModel" [(ngModel)]="dataForm.customer.facebook" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>IG</mat-label>
<mat-form-field>
<input matInput name="ig" #ig="ngModel" [(ngModel)]="dataForm.customer.ig" disabled>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลร้านค้า</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-2 md:col-span-12 ">
<mat-label>คำนำหน้า</mat-label>
<ng-select placeholder="เลือกคำนำหน้า" name="sellerprefix" #sellerprefix="ngModel" [(ngModel)]="dataForm.seller.prefix" appendTo="body" disabled>
<ng-option *ngFor="let item of prefixData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อลูกค้า</mat-label>
<mat-form-field>
<input matInput name="sellerfirstName" #sellerfirstName="ngModel" [(ngModel)]="dataForm.seller.firstName" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>นามสกุล</mat-label>
<mat-form-field>
<input matInput name="sellerlastName" #sellerlastName="ngModel" [(ngModel)]="dataForm.seller.lastName" disabled>
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>เพศ</mat-label>
<ng-select placeholder="" name="sellergender" #sellergender="ngModel" [(ngModel)]="dataForm.seller.gender" appendTo="body" disabled>
<ng-option *ngFor="let item of genderData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เบอร์โทร</mat-label>
<mat-form-field>
<input matInput name="sellerPhone" #sellerPhone="ngModel" [(ngModel)]="dataForm.seller.phone" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>Facebook</mat-label>
<mat-form-field>
<input matInput name="sellerFacebook" #sellerFacebook="ngModel" [(ngModel)]="dataForm.seller.facebook" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID Line</mat-label>
<mat-form-field>
<input matInput name="sellerLine" #sellerLine="ngModel" [(ngModel)]="dataForm.seller.line" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID LINE ร้านค้า</mat-label>
<mat-form-field>
<input matInput name="sellerLineShop" #sellerLineShop="ngModel" [(ngModel)]="dataForm.seller.lineShop" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>IG</mat-label>
<mat-form-field>
<input matInput name="sellerIg" #sellerIg="ngModel" [(ngModel)]="dataForm.seller.ig" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>S/N สินค้า</mat-label>
<mat-form-field>
<input matInput name="sellerSnProduct" #sellerSnProduct="ngModel" [(ngModel)]="dataForm.seller.snProduct" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>แหล่งที่มา</mat-label>
<mat-form-field>
<input matInput name="source" #source="ngModel" [(ngModel)]="dataForm.source" disabled>
</mat-form-field>
</div>
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>ค่าขนส่ง</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="shippingCost" #shippingCost="ngModel" [(ngModel)]="dataForm.shippingCost" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
</div>
</div>
</div>
<div class="card card-table card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลสินค้า</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mb-2">
<div class="col-span-3 md:col-span-12 ">
<mat-label>Condition</mat-label>
<ng-select placeholder="Condition" name="condition" #condition="ngModel" [(ngModel)]="dataForm.productCondition" disabled>
<ng-option *ngFor="let item of conditions" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-3 md:col-span-12 ">
<mat-label>Year</mat-label>
<mat-form-field>
<input appNumberOnly matInput name="year" #year="ngModel" [(ngModel)]="dataForm.productYear" disabled>
</mat-form-field>
</div>
</div>
<div class="table-wrap">
<table class="tables ">
<thead>
<tr>
<th>BOM</th>
<th>Model</th>
<th>Brand</th>
<th>Main</th>
<th>น้ำหนัก</th>
<th>Color</th>
<th>Year</th>
<th>ราคาสินค้า</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center">{{dataForm.productNo}}</td>
<td class="text-center">{{dataForm.productName }}</td>
<td class="text-center">{{dataForm.productBrandName}}</td>
<td class="text-center">{{dataForm.productSize }}</td>
<td class="text-center">{{dataForm.productWeight }}</td>
<td class="text-center">{{dataForm.productColor }}</td>
<td class="text-center">{{dataForm.productYear }}</td>
<td class="text-center"> <div class="b-color-orange">{{dataForm.price | number : '1.2-2'}}</div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">รูปสินค้าจากลูกค้า</div>
</div>
</div>
<div class="card-body">
<div class="list-images">
<div class=" grid grid-cols-12 gap-2 md:gap-2 items-center">
<ng-container *ngFor="let item of attachments; let i = index">
<div class="col-span-2 md:col-span-4">
<div class="flex justify-center items-center list-images-item">
<div class="list-images-action">
<i *ngIf="dataForm.coverImage !== item" class="bi bi-star color-main cursor-pointer select-none"></i>
<i *ngIf="dataForm.coverImage === item" class="bi bi bi-star-fill color-main cursor-pointer select-none"></i>
</div>
<img src="{{storage.products}}/{{item}}" alt="">
</div>
</div>
</ng-container>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">อุปกรณ์</div>
</div>
</div>
<div class="card-body">
<ng-container *ngFor="let item of equipment; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-9 md:col-span-8 " >{{item}}</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="equipment.includes('อื่นๆ')">
<ng-container *ngFor="let item of equipmentOther; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-9 md:col-span-8 " >{{item}}</div>
</div>
</div>
</ng-container>
</ng-container>
<div style="height: 20px;"></div>
</div>
</div>
</ng-container>
<ng-container *ngIf="isTabs === 2">
<div class="grid grid-cols-12 gap-4 md:gap-2 ">
<div class="col-span-6 md:col-span-12">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลการจัดผ่อน</div>
</div>
<div class="card-body form-input-list">
<div style="height: 20px;"></div>
<div class="grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> วันที่เริ่มจัดผ่อน</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input
matInput
name="startDate"
#startDate="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataForm.startDate"
[matDatepicker]="dpkName"
readonly
disabled
/>
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit"></div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ราคา (Price)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="price" #price="ngModel" [(ngModel)]="dataForm.price" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำแม่ค้า</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="sellerDeposit" #sellerDeposit="ngModel" [(ngModel)]="dataForm.sellerDeposit" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำ CMFS</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="deposit" #deposit="ngModel" [(ngModel)]="dataForm.deposit" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> เงินต้นคงเหลือ (Total)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="principalBalanceTotal" #principalBalanceTotal="ngModel" [(ngModel)]="dataForm.principalBalanceTotal" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ต้องการผ่อน (Term)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput type="number" name="wantToInstallmentTerm" #wantToInstallmentTerm="ngModel" [(ngModel)]="dataForm.wantToInstallmentTerm" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">งวด</div>
</div>
</div>
<div style="height: 20px;"></div>
</div>
</div>
</div>
<div class="col-span-6 md:col-span-12">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> รายละเอียดค่าใช้จ่ายในการโอนเงิน</div>
</div>
<div class="card-body form-input-list">
<div style="height: 20px;"></div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำ CMFS Deposit</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="cmfsDeposit" #cmfsDeposit="ngModel" [(ngModel)]="dataForm.cmfsDeposit" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> หัก เงินมัดจำแม่ค้า </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="lessSellerDeposit" #lessSellerDeposit="ngModel" [(ngModel)]="dataForm.lessSellerDeposit" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Packing </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusPacking" #plusPacking="ngModel" [(ngModel)]="dataForm.plusPacking" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Luxury handbag </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusLuxuryHandbag" #plusLuxuryHandbag="ngModel" [(ngModel)]="dataForm.plusLuxuryHandbag" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Bank fee, Insurance , Storage</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusBankFee" #plusBankFee="ngModel" [(ngModel)]="dataForm.plusBankFee" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ส่วนลด </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="discount" #discount="ngModel" [(ngModel)]="dataForm.discount" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> สรุปยอดโอน </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="transferSummary" #transferSummary="ngModel" [(ngModel)]="dataForm.transferSummary" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div style="height: 10px;"></div>
</div>
</div>
</div>
</div>
<div class="card card-table mb-6">
<div class="card-body">
<div class="table-wrap" *ngIf="dataForm.quotationDetail?.[0]">
<table class="tables">
<thead>
<tr>
<th>งวดที่</th>
<th>กำหนดจ่ายวันที่ <br>Due date</th>
<th>เงินต้น <br>Principle</th>
<th>ดอกเบี้ย(บาท) <br>Interest Total</th>
<th>Bank fee, <br>Insurance ,Storage</th>
<th>รวมยอดจ่ายต่อเดือน <br>Total payment</th>
<th>เงินต้นคงเหลือ <br>Principle Total</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let item of dataForm.quotationDetail; let i = index">
<tr>
<td class="text-center">{{item.installment }}</td>
<td class="text-center">{{item.dueDate | date : 'dd/MM/YYYY'}}</td>
<td class="text-center">{{item.principle | number : '1.0-0'}}</td>
<td class="text-center">{{item.interestTotal | number : '1.0-0'}}</td>
<td class="text-center">{{item.fee | number : '1.0-0'}}</td>
<td class="text-center"><span class="b-color-green">{{item.totalPayment | number : '1.0-0'}}</span></td>
<td class="text-center"><span class="b-color-orange" *ngIf="item.principleTotal">{{item.principleTotal | number : '1.0-0'}}</span></td>
</tr>
</ng-container>
<tr>
<td colspan="2" class="text-right"><b>รวม</b></td>
<td class="text-center">{{dataForm.principleSum | number : '1.0-0'}}</td>
<td class="text-center">{{dataForm.interestTotalSum | number : '1.0-0'}}</td>
<td class="text-center">{{dataForm.feeSum | number : '1.0-0'}}</td>
<td class="text-center"><span class="b-color-green">{{dataForm.totalPaymentSum | number : '1.0-0'}}</span></td>
<td class="text-center"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="isTabs === 3">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลธนาคารที่รับชำระเงิน</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-8 md:col-span-12 ">
<mat-label>ชื่อธนาคาร</mat-label>
<mat-form-field>
<input matInput name="productNo" #productNo="ngModel" [(ngModel)]="dataForm.contractBankName" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:hidden"></div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อบัญชี</mat-label>
<mat-form-field>
<input matInput name="contractAccountName" #contractAccountName="ngModel" [(ngModel)]="dataForm.contractAccountName" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เลขที่บัญชี</mat-label>
<mat-form-field>
<input matInput name="contractAccountNumber" #contractAccountNumber="ngModel" [(ngModel)]="dataForm.contractAccountNumber" disabled>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> รายละเอียดท้ายสัญญา</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-12 md:col-span-12 ">
<mat-form-field>
<textarea matInput [(ngModel)]="dataForm.contractDetail" name="contractDetail" #contractDetail="ngModel" placeholder="" disabled></textarea>
</mat-form-field>
</div>
</div>
</div>
</div>
</ng-container>
<div class="main-form-action text-right">
<ng-container *ngIf="dataForm.statusContract === 'pending' ">
<button type="button" class="btn btn-red" (click)="onSubmitCancel(ngf)">ไม่อนุมัติ</button>
<button type="submit" class="btn btn-submit">อนุมัติ</button>
</ng-container>
<button type="button" class="btn btn-back" (click)="onAction('back')">ยกเลิก</button>
</div>
</form>

View File

@@ -0,0 +1,187 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import {API, CONDITIONS, EAction, EText, GENDER, PREFIX, SOURCES, STORAGE} from "../../../../@config/app";
import { AppService } from "../../../../app.service";
import { lastValueFrom } from "rxjs";
import { BaseFormComponent } from "../../../../@common/base/base-form.component";
import { ActivatedRoute, Router } from "@angular/router";
import { IProduct } from "../../../../app.interface";
import {sortByProperty} from "../../../../@common/utils/OrderBy";
import {C} from "@angular/cdk/keycodes";
@Component({
selector: "app-appraisal-3rd-time-do",
templateUrl: "./contract-approved-do.component.html",
styleUrls: []
})
export class ContractApprovedDoComponent extends BaseFormComponent implements OnInit {
override dataForm: any = {};
dataView: IProduct = {};
auth: any = {};
title = "";
api: any = API;
storage: any = STORAGE;
attachments: any = [];
equipment: any = [];
equipmentOther: any = [];
settings: any = [];
masterProductUnit: any = [];
deviation: any = 0;
isTabs: any = 1;
prefixData = PREFIX;
genderData = GENDER;
conditions = CONDITIONS;
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
public changeDetectorRef: ChangeDetectorRef,
public appService: AppService
) {
super();
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.ids = params["id"];
this.action = params["action"];
this.auth = this.appService.auth();
this.dataForm.customer = {};
this.dataForm.sellsr = {};
if (this.ids) await this.getData();
});
}
async onAction(action: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.BACK));
if (!sweetalert.isConfirmed) return;
if (action === "back") return this.router.navigate(["/pages/contract/approved/list", this.action]);
return;
}
async getData() {
if (!this.ids) this.appService.message(EAction.INFO, EText.NO_DATA);
try {
this.dataForm = await lastValueFrom(this.appService.get(`${this.api.quotation}/getById/${this.ids}`));
this.attachments = this.dataForm.images ? this.dataForm.images?.split(",") : [];
this.equipment = this.dataForm.equipment ? this.dataForm.equipment?.split(",") : [];
this.equipmentOther = this.dataForm.equipmentOther ? this.dataForm.equipmentOther?.split(",") : [];
this.dataForm.deposit = Number(this.dataForm.deposit) + Number(this.dataForm.sellerDeposit2ndTime) + Number(this.dataForm.sellerDeposit3rdTime);
if (!this.dataForm.customerId) {
this.dataForm.customer = {};
this.dataForm.customer.prefix = this.dataForm.customerPrefix;
this.dataForm.customer.firstName = this.dataForm.customerFirstName;
this.dataForm.customer.lastName = this.dataForm.customerLastName;
this.dataForm.customer.phone = this.dataForm.customerPhone;
}
sortByProperty(this.dataForm.quotationDetail, 'installment', 'ASC');
this.dataForm.principleSum = 0
this.dataForm.interestTotalSum = 0
this.dataForm.feeSum = 0
this.dataForm.feeSum = 0
this.dataForm.totalPaymentSum = 0
this.dataForm.quotationDetail.map((item : any) => {
this.dataForm.principleSum += Number(item.principle)
this.dataForm.interestTotalSum += Number(item.interestTotal)
this.dataForm.feeSum += Number(item.fee)
this.dataForm.totalPaymentSum += Number(item.totalPayment)
})
this.dataForm.principleSum = Math.round(this.dataForm.principleSum);
this.dataForm.contractBankName = 'ธนาคารทหารไทยธนชาต จำกัด (มหาชน)';
this.dataForm.contractAccountName = 'บริษัท ซีเอ็ม เอฟเอส จำกัด';
this.dataForm.contractAccountNumber = '263-2-17778-4';
this.dataForm.contractDetail = 'ชำระเงินงวดอย่างน้อย ทุกเดือนตามวันและยอดขั้นต่ำตามตาราง โดยที่ไม่เสียค่าปรับ ทั้งนี้หากเกินกำหนด ผู้กู้ต้องเสียค่าดอกเบี้ยผิดนัดเพิ่มเติมวันละ 1,000 บาท (ไม่รวมค่าทวงถาม) หากขาดส่งเกินกว่า 60 วัน นับแต่วันผ่อนล่าสุดจะถือว่าผิดสัญญา โดยหากผู้กู้ติดสัญญาไม่ว่ากรณีใดๆ ผู้กู้ยินดีที่จะนำสังหาริมทรัพย์ที่ผู้กู้นำเงินที่กู้ไปซื้อเป็นค่าตอบแทนในการชำระหนี้สินส่วนที่เหลือโดยทันที';
this.changeDetectorRef.detectChanges();
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onSubmitCancel(form: any) {
this.dataForm.isStatusContract = 'cancel';
await this.onSubmit(form)
}
async onSubmit(form: any) {
try {
// console.log(form);
// if (!form.valid) return false;
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.CREATE));
if (!sweetalert.isConfirmed) return;
if (this.dataForm.isStatusContract) {
this.dataForm.statusContract = 'cancel';
this.dataForm.contractCancelDate = new Date();
this.dataForm.contractCancelBy = this.auth.id;
}
if (!this.dataForm.isStatusContract) {
this.dataForm.statusContract = 'approved';
this.dataForm.contractApprovedDate = new Date();
this.dataForm.contractApprovedBy = this.auth.id;
this.dataForm.statusWarehouse = 'warehouse';
}
this.dataForm.images = this.attachments?.[0] ? this.attachments.join(",") : null;
return await this.onUpdate();
} catch (e) {
console.log(e);
}
}
async onUpdate() {
try {
await lastValueFrom(this.appService.post(`${this.api.quotation}/update/${this.ids}`, this.dataForm));
await this.appService.message(EAction.SUCCESS, EText.UPDATE);
await this.router.navigate(["/pages/contract/approved/list", this.action]);
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onAttachments($event: any, type: any) {
const file = $event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("ref", type);
formData.append("file", file);
try {
const res = await lastValueFrom(this.appService.post(`${this.api.attachments}/products`, formData));
if (!this.attachments[0]) {
this.dataForm.coverImage = res.fileName;
}
this.attachments.push(res.fileName);
console.log(this.attachments, res);
this.changeDetectorRef.detectChanges();
} catch (e) {
this.appService.message(EText.ERROR);
}
}
async onRemoveAttachments(i: number, fileName: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
// await lastValueFrom(this.appService.delete(`${this.api.attachments}/deleteByName`, fileName));
this.attachments?.splice(i, 1);
if (!this.attachments[0]) {
this.dataForm.coverImage = null;
}
this.changeDetectorRef.detectChanges();
}
protected readonly SOURCES = SOURCES;
}

View File

@@ -0,0 +1,205 @@
<div class="card card-table">
<div class="card-filter text-right">
<div class="card-filter-section grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-5 md:col-span-12 md:order-2">
<mat-form-field>
<i matTextPrefix class="bi bi-search"></i>
<input matInput name="keyword" #keyword="ngModel" [(ngModel)]="dataFilter.keyword" (ngModelChange)="onFilter($event)">
</mat-form-field>
</div>
<div class="col-span-7 md:col-span-12 md:order-1">
<button type="button" class="btn btn-export" (click)="onExport()">Export</button>
</div>
</div>
<div class="card-filter-section grid grid-cols-12 gap-4 items-center md:gap-2 ">
<div class="col-span-6 md:col-span-12">
<div class="tabs-btn">
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'pending'}" (click)="onTabs('pending')">รออนุมัติ</button>
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'approved'}" (click)="onTabs('approved')">อนุมัติแล้ว</button>
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'cancel'}" (click)="onTabs('cancel')">ไม่อนุมัติ</button>
</div>
</div>
<div class="col-span-6 md:col-span-12 ">
<div class="flex w-full justify-end md:justify-start">
<div class="">จำนวนทั้งหมด {{totalItem}} รายการ</div>
<div class="pl-2 pr-2">|</div>
<div class="">ค้นหาจำนวน {{totalOfElement}} รายการ</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-wrap">
<table class="table table-main" mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<ng-container matColumnDef="quotationNo">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>เลขที่ใบเสนอราคา</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.quotationNo}}</td>
</ng-container>
<ng-container matColumnDef="customerFirstName">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>ชื่อลูกค้า</th>
<td mat-cell *matCellDef="let item" width="150" class="">
<ng-container *ngIf="item.customerId"> {{item.customer?.prefix}}{{item.customer?.firstName}} {{item.customer?.lastName}}</ng-container>
<ng-container *ngIf="!item.customerId">{{item.customerFirstName}} {{item.customerLastName}}</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="productNo">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>BOM</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.productNo}}</td>
</ng-container>
<ng-container matColumnDef="productName">
<th mat-header-cell *matHeaderCellDef class="tal">Model</th>
<td mat-cell *matCellDef="let item" class="" style="min-width: 220px;">{{item.productName }}</td>
</ng-container>
<ng-container matColumnDef="productBrandName">
<th mat-header-cell *matHeaderCellDef class="tal">Brand</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productBrandName }}</td>
</ng-container>
<ng-container matColumnDef="productSize">
<th mat-header-cell *matHeaderCellDef class="tal">Main</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productSize }}</td>
</ng-container>
<ng-container matColumnDef="productWeight">
<th mat-header-cell *matHeaderCellDef class="tal" >น้ำหนัก</th>
<td mat-cell *matCellDef="let item" class="">{{item.productWeight }}</td>
</ng-container>
<ng-container matColumnDef="productColor">
<th mat-header-cell *matHeaderCellDef class="tal">Color</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productColor }}</td>
</ng-container>
<ng-container matColumnDef="productYear">
<th mat-header-cell *matHeaderCellDef class="tal">Year</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productYear }}</td>
</ng-container>
<ng-container matColumnDef="userFullName">
<th mat-header-cell *matHeaderCellDef class="tal">ชื่อคนขาย</th>
<td mat-cell *matCellDef="let item" style="min-width: 200px;" >{{item.userFullName }}</td>
</ng-container>
<ng-container matColumnDef="price">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-green"> {{item.price | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="deposit">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนมัดจำ</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-orange"> {{item.deposit | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="wantToInstallmentTerm">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ระยะเวลาผ่อน</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.wantToInstallmentTerm }} งวด</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่บันทึก</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.createdDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="contractApprovedDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่อนุมัติ</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.contractApprovedDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="contractCancelDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่ไม่อนุมัติ</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.contractCancelDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef class="tac">สถานะ</th>
<td mat-cell *matCellDef="let item" class="tac" style="min-width: 100px;">
<div *ngIf="item.status === 'paid' " class="status status-active">ชำระแล้ว</div>
<div *ngIf="item.status === 'pending'" class="status status-disabled">รอชำระ</div>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ประเภทการชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.type === 'deposit' " class="status-text status-deposit">ค่ามัดจำ</div>
<div *ngIf="item.type === 'installment'" class="status-text status-installment">ผ่อนสินค้า</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentType">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ประเภทการชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.paymentType === 'deposit' " class="status-text status-deposit">ค่ามัดจำ</div>
<div *ngIf="item.paymentType === 'installment'" class="status-text status-installment">ผ่อนสินค้า</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentMethod">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>วิธีชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.paymentMethod === 'transfer' " class="status-text status-transfer">โอนเงิน</div>
<div *ngIf="item.paymentMethod === 'cash'" class="status-text status-cash">เงินสด</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentAmountAll">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="tac">
<div class="b-color-green">{{item.paymentAmountAll | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="contractBy">
<th mat-header-cell *matHeaderCellDef class="tac">พนักงานทำรายการ</th>
<td mat-cell *matCellDef="let item" class="tac">{{item?.userContractBy?.name}}</td>
</ng-container>
<ng-container matColumnDef="contractApprovedBy">
<th mat-header-cell *matHeaderCellDef class="tac">ผู้อนุมัติ</th>
<td mat-cell *matCellDef="let item" class="tac">{{item?.userApprovedBy?.name }}</td>
</ng-container>
<ng-container matColumnDef="contractCancelBy">
<th mat-header-cell *matHeaderCellDef class="tac">ผู้ไม่อนุมัติ</th>
<td mat-cell *matCellDef="let item" class="tac">{{item?.userCancelBy?.name }}</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef width="80">Action</th>
<td mat-cell *matCellDef="let item">
<div class="action flex justify-center">
<div class="item" *ngIf="item.statusContract === 'pending' " >
<i class="bi bi-pencil-square icon-edit" (click)="onAction('do', item.id)"></i>
</div>
<div class="item" *ngIf="['approved', 'cancel'].includes(item.statusContract) ">
<i class="bi bi-eye color-green" (click)="onAction('do', item.id)"></i>
</div>
<div class="item" *ngIf="['approved', 'cancel'].includes(item.statusContract) ">
<i class="bi bi-filetype-pdf color-main" (click)="onAction('pdf', item.id)"></i>
</div>
</div>
</td>
</ng-container>
</table>
<div *ngIf="dataSource?.length === 0" class="no-data"></div>
</div>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons (page)="getData($event)"></mat-paginator>
</div>
</div>

View File

@@ -0,0 +1,125 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API, EAction, EStatusContract, EStatusQuotation, EText } from "../../../../@config/app";
import { ActivatedRoute, Router } from "@angular/router";
import generateParamsValue from "../../../../@common/utils/GenerateParamsValue";
@Component({
selector: "app-appraisal-3rd-time-index",
templateUrl: "./contract-approved-index.component.html",
styleUrls: []
})
export class ContractApprovedIndexComponent extends BaseListComponent implements OnInit {
pageTitle = "สัญญา";
action = "pending";
apiUrl: string = API.quotation;
api: any = API;
displayedColumns: string[] = [];
masterProductCategory: any = [];
masterProductBrand: any = [];
filterKeyword: Subject<string> = new Subject<string>();
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService,
public activatedRoute: ActivatedRoute,
public changeDetectorRef: ChangeDetectorRef
) {
super();
this.filterKeyword.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(model => {
this.getData();
});
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.action = params["action"];
if (!this.action) this.router.navigate(["/pages/contract/approved/list", EStatusQuotation.PENDING]);
await this.getData();
});
}
async onTabs(action?: any) {
this.dataFilter = {};
return this.router.navigate(["/pages/contract/approved/list", action]);
}
onAction(action : any, id?: any) {
if (action === 'do') return this.router.navigate([`/pages/contract/approved/do/${this.action}`, id]);
if (action === 'pdf') return this.router.navigate([`/pages/contract/approved/pdf/${this.action}`, id]);
return;
}
async getData($event?: any) {
try {
this.dataSource = [];
this.dataFilter.step = 5;
this.dataFilter.statusContract = this.action;
let url = API.quotation;
if (this.action === EStatusContract.PENDING) {
url = API.quotation;
this.displayedColumns = ["action", "price", "wantToInstallmentTerm", "customerFirstName", "productNo", "productName", "productBrandName", "productSize", "productWeight", "productColor","contractBy", "createdDate"];
}
if (this.action === EStatusContract.APPROVED) {
url = API.quotation;
this.displayedColumns = ["action", "price", "wantToInstallmentTerm", "customerFirstName", "productNo", "productName", "productBrandName", "productSize", "productWeight", "productColor","contractBy", "contractApprovedBy", "contractApprovedDate"];
}
if (this.action === EStatusContract.CANCEL) {
url = API.quotation;
this.displayedColumns = ["action", "price", "wantToInstallmentTerm", "customerFirstName", "productNo", "productName", "productBrandName", "productSize", "productWeight", "productColor","contractBy", "contractCancelBy", "contractCancelDate"];
}
this.dataFilter.keywordColumn = "quotationNo,productNo,customerFirstName,customerLastName,price,productName";
const dataSource = await lastValueFrom(this.appService.get(this.setParams(url, $event)));
this.dataSource = this.setDataSource<any>(dataSource);
} catch (e) {
this.dataSource = [];
}
}
onFilter($event?: any) {
this.filterKeyword.next($event);
}
clearDate($event?: any) {
$event.stopPropagation();
this.dataFilter.createdDate = null;
}
async onSort($event: any) {
this.dataFilter.orderBy = $event.active;
this.dataFilter.sort = $event.direction;
await this.getData();
console.log($event);
}
async onDelete(ids: any) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
try {
await lastValueFrom(this.appService.delete(this.apiUrl, ids));
await this.appService.message(EAction.SUCCESS, EText.DELETE);
await this.getData(this.getCurrentPage());
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
onExport() {
const filter = generateParamsValue(this.dataFilter);
const url = `${API.quotation}/export-contract-approved?${filter ? '&' + filter : '' }`;
window.open(url);
}
}

View File

@@ -0,0 +1,3 @@
<mat-progress-bar *ngIf="!pdfView" mode="indeterminate"></mat-progress-bar>
<iframe *ngIf="pdfView" [src]="pdfView"></iframe>

View File

@@ -0,0 +1,114 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { lastValueFrom } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API, STORAGE } from "../../../../@config/app";
import { ActivatedRoute, Router } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
import { BaseFormComponent } from "../../../../@common/base/base-form.component";
import { IQuotation } from "../../../../@common/interface/Quotation";
import { format, parseISO } from "date-fns";
@Component({
selector: "app-contract-make-pdf-index",
templateUrl: "./contract-approved-pdf.component.html",
styleUrls: []
})
export class ContractApprovedPdfComponent extends BaseFormComponent implements OnInit {
pageTitle = "สัญญา";
apiUrl: string = API.quotation;
api: any = API;
dataView: any;
pdfView: any;
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
public changeDetectorRef: ChangeDetectorRef,
public appService: AppService,
private sanitizer: DomSanitizer
) {
super();
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.ids = params["id"];
await this.getData();
});
}
async getData() {
try {
const quotation : IQuotation = await lastValueFrom(this.appService.get(`${this.api.quotation}/getById/${this.ids}`));
const startDate = quotation.startDate ? format(parseISO(quotation.startDate), "dd/MM/yyyy") : null;
const customerPrefix = quotation.customerPrefix ? quotation.customerPrefix : '';
const customerName = quotation.customerId ? `${quotation.customer?.prefix} ${quotation.customer?.firstName} ${quotation.customer?.lastName}` :
`${customerPrefix} ${quotation.customerFirstName} ${quotation.customerLastName}`;
const data = {
doc_no: quotation.quotationNo,
product_code: quotation.productNo,
customer_name: customerName,
first_name: quotation.customerFirstName,
last_name: quotation.customerLastName,
start_date: startDate,
phone_no: quotation.customerPhone,
picture: `${STORAGE.products}/${quotation.coverImage}`,
price: Number(quotation.price),
deposit: Number(quotation.deposit),
seller_deposit: Number(quotation.sellerDeposit),
cmfs_deposit: Number(quotation.cmfsDeposit),
total_balance: Number(quotation.principalBalanceTotal),
installment: Number(quotation.wantToInstallmentTerm),
packing: Number(quotation.plusPacking),
luxury_handbag_authentication: Number(quotation.plusLuxuryHandbag),
bankfee_insurance_storage: Number(quotation.plusBankFee),
transfer_amount: Number(quotation.transferSummary),
deduct_seller_deposit: Number(quotation.sellerDepositSum),
authenticity_verification: 0,
data: [],
total1: 0,
total2: 0,
total3: 0,
total4: 0
}
const quotationDetail: any = [];
quotation.quotationDetail?.map(item => {
const dueDate = item.dueDate ? format(parseISO(item.dueDate), "dd/MM/yyyy") : null;
const map = {
due_date: dueDate,
principle: Number(item.principle),
interest_total: Number(item.interestTotal),
bank_fee: Number(item.fee),
total_payment: Number(item.totalPayment),
principle_total: Number(item.principleTotal)
}
quotationDetail.push(map);
})
data.data = quotationDetail;
const pdf = await lastValueFrom(this.appService.post(`${this.api.installmentContractReport}/pdf`, data, { responseType: "arraybuffer" }));
const url = URL.createObjectURL(new Blob([pdf], { type: "application/pdf" }));
this.pdfView = this.sanitizer.bypassSecurityTrustResourceUrl(url);
} catch (e) {
console.log(e);
}
}
}

View File

@@ -0,0 +1,28 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { ContractMakeIndexComponent } from "./index/contract-make-index.component";
import { ContractMakeDoComponent } from "./do/contract-make-do.component";
import { ContractMakePdfComponent } from "./pdf/contract-make-pdf.component";
const routes: Routes = [
{ path: "", component: ContractMakeIndexComponent },
{ path: "list", component: ContractMakeIndexComponent },
{ path: "list/:action", component: ContractMakeIndexComponent },
{ path: "do/:action", component: ContractMakeDoComponent },
{ path: "do/:action/:id", component: ContractMakeDoComponent },
{ path: "pdf/:action/:id", component: ContractMakePdfComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RoutingModule {
}
export const RoutingComponents = [
ContractMakeIndexComponent,
ContractMakeDoComponent,
ContractMakePdfComponent
];

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import {RoutingComponents, RoutingModule} from './contract-make-routing.module';
import {AppSharedModule} from "../../../app.shared";
import { NgOptimizedImage } from "@angular/common";
@NgModule({
declarations: [
...RoutingComponents,
],
imports: [
AppSharedModule,
RoutingModule,
NgOptimizedImage
]
})
export class ContractMakeModule {}

View File

@@ -0,0 +1,867 @@
<ul class="progressbar">
<li [ngClass]="{ 'active' : isTabs === 1}" (click)="isTabs = 1">รายละเอียดหลัก</li>
<li [ngClass]="{ 'active' : isTabs === 2}" (click)="isTabs = 2">การจัดผ่อน</li>
<li [ngClass]="{ 'active' : isTabs === 3}" (click)="isTabs = 3">ข้อมูลรับชำระเงิน</li>
</ul>
<form class="main-form" #ngf="ngForm" (ngSubmit)="onSubmit(ngf)">
<ng-container *ngIf="isTabs === 1">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> สัญญาเงินกู้ระหว่าง CM-FS. Co., Ltd. ("บริษัทฯ") กับ</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-4 md:col-span-12 ">
<mat-label>เลขที่สัญญา/BOM</mat-label>
<mat-form-field>
<input matInput name="productNo" #productNo="ngModel" [(ngModel)]="dataForm.productNo" disabled>
</mat-form-field>
</div>
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>ชื่อลูกค้า</mat-label>-->
<!-- <mat-form-field>-->
<!-- <div class="flex">-->
<!-- <input style="width: 75px; padding-right: 0 !important;" matInput name="customerPrefix" #customerPrefix="ngModel" [(ngModel)]="dataForm.customer.prefix" disabled>-->
<!-- <input style="padding-left: 0 !important;" matInput name="customerFirstName" #customerFirstName="ngModel" [(ngModel)]="dataForm.customer.firstName" disabled>-->
<!-- </div>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>นามสกุล</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="customerLastName" #customerLastName="ngModel" [(ngModel)]="dataForm.customer.lastName" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>เบอร์โทร</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="customerPhone" #customerPhone="ngModel" [(ngModel)]="dataForm.customer.phone" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>เลขบัตรประชาชน</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="customerIdCard" #customerIdCard="ngModel" [(ngModel)]="dataForm.customer.idCard" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<div class="col-span-4 md:col-span-12 ">
<mat-label>วันที่ทำสัญญา</mat-label>
<mat-form-field>
<input
matInput
name="contractDate"
#contractDate="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataForm.contractDate"
[matDatepicker]="dpkName"
readonly
required
/>
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-4 md:hidden"></div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>คำนำหน้า</mat-label>
<ng-select placeholder="เลือกคำนำหน้า" name="prefix" #prefix="ngModel" [(ngModel)]="dataForm.customer.prefix" appendTo="body" disabled>
<ng-option *ngFor="let item of prefixData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อลูกค้า</mat-label>
<mat-form-field>
<input matInput name="firstName" #firstName="ngModel" [(ngModel)]="dataForm.customer.firstName" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>นามสกุล</mat-label>
<mat-form-field>
<input matInput name="lastName" #lastName="ngModel" [(ngModel)]="dataForm.customer.lastName" disabled>
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>เพศ</mat-label>
<ng-select placeholder="" name="gender" #gender="ngModel" [(ngModel)]="dataForm.customer.gender" appendTo="body" disabled>
<ng-option *ngFor="let item of genderData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เบอร์โทร</mat-label>
<mat-form-field>
<input matInput name="phone" #phone="ngModel" [(ngModel)]="dataForm.customer.phone" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เลขบัตรประชาชน</mat-label>
<mat-form-field>
<input matInput name="idCard" #idCard="ngModel" [(ngModel)]="dataForm.customer.idCard" disabled>
</mat-form-field>
</div>
<div class="col-span-12 md:col-span-12 ">
<mat-label>ที่อยู่ตามบัตรประชาชน</mat-label>
<mat-form-field>
<input matInput name="address" #address="ngModel" [(ngModel)]="dataForm.customer.address" disabled>
</mat-form-field>
</div>
<div class="col-span-12 md:col-span-12 ">
<mat-label>ที่อยู่ในการจัดส่ง</mat-label>
<label class="inline-flex items-center cursor-pointer select-none ml-2">
<input type="checkbox" name="isAddress" [(ngModel)]="dataForm.customer.isAddress" disabled>
<span style="padding-left: 2px;">ใช้ที่อยู่ตามบัตรประชาชน</span>
</label>
<mat-form-field>
<input matInput name="deliveryAddress" #deliveryAddress="ngModel" [(ngModel)]="dataForm.customer.deliveryAddress" [disabled]="dataForm.customer.isAddress" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>E-mail</mat-label>
<mat-form-field>
<input matInput name="email" #email="ngModel" [(ngModel)]="dataForm.customer.email" disabled>
</mat-form-field>
</div>
<div class="col-span-8 md:col-span-12 ">
<mat-label>อาชีพ</mat-label>
<mat-form-field>
<input matInput name="occupation" #occupation="ngModel" [(ngModel)]="dataForm.customer.occupation" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID Line</mat-label>
<mat-form-field>
<input matInput name="line" #line="ngModel" [(ngModel)]="dataForm.customer.line" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>Facebook</mat-label>
<mat-form-field>
<input matInput name="facebook" #facebook="ngModel" [(ngModel)]="dataForm.customer.facebook" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>IG</mat-label>
<mat-form-field>
<input matInput name="ig" #ig="ngModel" [(ngModel)]="dataForm.customer.ig" disabled>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลร้านค้า</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>ชื่อคนขาย/ร้านค้า</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="sellerName" #sellerName="ngModel" [(ngModel)]="dataForm.seller.name" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<div class="col-span-2 md:col-span-12 ">
<mat-label>คำนำหน้า</mat-label>
<ng-select placeholder="เลือกคำนำหน้า" name="sellerprefix" #sellerprefix="ngModel" [(ngModel)]="dataForm.seller.prefix" appendTo="body" disabled>
<ng-option *ngFor="let item of prefixData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อลูกค้า</mat-label>
<mat-form-field>
<input matInput name="sellerfirstName" #sellerfirstName="ngModel" [(ngModel)]="dataForm.seller.firstName" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>นามสกุล</mat-label>
<mat-form-field>
<input matInput name="sellerlastName" #sellerlastName="ngModel" [(ngModel)]="dataForm.seller.lastName" disabled>
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>เพศ</mat-label>
<ng-select placeholder="" name="sellergender" #sellergender="ngModel" [(ngModel)]="dataForm.seller.gender" appendTo="body" disabled>
<ng-option *ngFor="let item of genderData" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เบอร์โทร</mat-label>
<mat-form-field>
<input matInput name="sellerPhone" #sellerPhone="ngModel" [(ngModel)]="dataForm.seller.phone" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>Facebook</mat-label>
<mat-form-field>
<input matInput name="sellerFacebook" #sellerFacebook="ngModel" [(ngModel)]="dataForm.seller.facebook" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID Line</mat-label>
<mat-form-field>
<input matInput name="sellerLine" #sellerLine="ngModel" [(ngModel)]="dataForm.seller.line" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ID LINE ร้านค้า</mat-label>
<mat-form-field>
<input matInput name="sellerLineShop" #sellerLineShop="ngModel" [(ngModel)]="dataForm.seller.lineShop" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>IG</mat-label>
<mat-form-field>
<input matInput name="sellerIg" #sellerIg="ngModel" [(ngModel)]="dataForm.seller.ig" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>S/N สินค้า</mat-label>
<mat-form-field>
<input matInput name="sellerSnProduct" #sellerSnProduct="ngModel" [(ngModel)]="dataForm.seller.snProduct" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>แหล่งที่มา</mat-label>
<mat-form-field>
<input matInput name="source" #source="ngModel" [(ngModel)]="dataForm.source" disabled>
</mat-form-field>
</div>
<!-- <div class="col-span-4 md:col-span-12 ">-->
<!-- <mat-label>ค่าขนส่ง</mat-label>-->
<!-- <mat-form-field>-->
<!-- <input matInput name="shippingCost" #shippingCost="ngModel" [(ngModel)]="dataForm.shippingCost" disabled>-->
<!-- </mat-form-field>-->
<!-- </div>-->
</div>
</div>
</div>
<div class="card card-table card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลสินค้า</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mb-2">
<div class="col-span-3 md:col-span-12 ">
<mat-label>Condition</mat-label>
<ng-select placeholder="Condition" name="condition" #condition="ngModel" [(ngModel)]="dataForm.productCondition" >
<ng-option *ngFor="let item of conditions" [value]="item">{{item}}</ng-option>
</ng-select>
</div>
<div class="col-span-3 md:col-span-12 ">
<mat-label>Year</mat-label>
<mat-form-field>
<input appNumberOnly matInput name="year" #year="ngModel" [(ngModel)]="dataForm.productYear" >
</mat-form-field>
</div>
</div>
<div class="table-wrap">
<table class="tables ">
<thead>
<tr>
<th>BOM</th>
<th>Model</th>
<th>Brand</th>
<th>Main</th>
<th>น้ำหนัก</th>
<th>Color</th>
<th>Year</th>
<th>ราคาสินค้า</th>
<th>จำนวนเงินมัดจำ</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center">{{dataForm.productNo}}</td>
<td class="text-center">{{dataForm.productName }}</td>
<td class="text-center">{{dataForm.productBrandName}}</td>
<td class="text-center">{{dataForm.productSize }}</td>
<td class="text-center">{{dataForm.productWeight }}</td>
<td class="text-center">{{dataForm.productColor }}</td>
<td class="text-center">{{dataForm.productYear }}</td>
<td class="text-center">{{dataForm.price | number : '1.2-2'}}</td>
<td class="text-center">
<div class="b-color-orange">{{dataForm.deposit | number : '1.2-2'}}</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">รูปสินค้าจากลูกค้า</div>
<div class="ml-4">
<input hidden type="file" accept="image/*" #productImages (change)="onAttachments($event, 'products')" />
<button type="button" class="btn btn-sm btn-success-o" (click)="productImages.click()">เพิ่มรูปภาพสินค้า</button>
</div>
</div>
</div>
<div class="card-body">
<div class="list-images">
<div class=" grid grid-cols-12 gap-2 md:gap-2 items-center">
<ng-container *ngFor="let item of attachments; let i = index">
<div class="col-span-2 md:col-span-4">
<div class="flex justify-center items-center list-images-item">
<div class="list-images-action">
<i *ngIf="dataForm.coverImage !== item" (click)="dataForm.coverImage = item"
matTooltip="ใช้ทำเอกสาร" class="bi bi-star color-main cursor-pointer select-none"></i>
<i *ngIf="dataForm.coverImage === item" class="bi bi bi-star-fill color-main cursor-pointer select-none"></i>
<i (click)="onRemoveAttachments(i, item)" class="bi bi-x-circle color-red cursor-pointer select-none"></i>
</div>
<img src="{{storage.products}}/{{item}}" alt="">
</div>
</div>
</ng-container>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title flex items-center">
<div class="">อุปกรณ์</div>
</div>
</div>
<div class="card-body">
<ng-container *ngFor="let item of equipment; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-9 md:col-span-8 " >{{item}}</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="equipment.includes('อื่นๆ')">
<ng-container *ngFor="let item of equipmentOther; let i = index">
<div class="form-list">
<div class="form-list-item grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-9 md:col-span-8 " >{{item}}</div>
</div>
</div>
</ng-container>
</ng-container>
<div style="height: 20px;"></div>
</div>
</div>
</ng-container>
<ng-container *ngIf="isTabs === 2">
<div class="grid grid-cols-12 gap-4 md:gap-2 ">
<div class="col-span-6 md:col-span-12">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลการจัดผ่อน</div>
</div>
<div class="card-body form-input-list">
<div style="height: 20px;"></div>
<div class="grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> วันที่เริ่มจัดผ่อน</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input
matInput
name="startDate"
#startDate="ngModel"
(click)="dpkName.open()"
[(ngModel)]="dataForm.startDate"
[matDatepicker]="dpkName"
readonly
disabled
/>
<mat-datepicker-toggle [for]="dpkName" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit"></div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ราคา (Price)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="price" #price="ngModel" [(ngModel)]="dataForm.price" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำแม่ค้า</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="sellerDeposit" #sellerDeposit="ngModel" [(ngModel)]="dataForm.sellerDeposit" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำ CMFS</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="deposit" #deposit="ngModel" [(ngModel)]="dataForm.deposit" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> เงินต้นคงเหลือ (Total)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="principalBalanceTotal" #principalBalanceTotal="ngModel" [(ngModel)]="dataForm.principalBalanceTotal" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ต้องการผ่อน (Term)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput type="number" name="wantToInstallmentTerm" #wantToInstallmentTerm="ngModel" [(ngModel)]="dataForm.wantToInstallmentTerm" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">งวด</div>
</div>
</div>
<div style="height: 20px;"></div>
</div>
</div>
</div>
<div class="col-span-6 md:col-span-12">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> รายละเอียดค่าใช้จ่ายในการโอนเงิน</div>
</div>
<div class="card-body form-input-list">
<div style="height: 20px;"></div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> มัดจำ CMFS Deposit</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="cmfsDeposit" #cmfsDeposit="ngModel" [(ngModel)]="dataForm.cmfsDeposit" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> หัก เงินมัดจำแม่ค้า </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="lessSellerDeposit" #lessSellerDeposit="ngModel" [(ngModel)]="dataForm.lessSellerDeposit" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Packing </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusPacking" #plusPacking="ngModel" [(ngModel)]="dataForm.plusPacking" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Luxury handbag </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusLuxuryHandbag" #plusLuxuryHandbag="ngModel" [(ngModel)]="dataForm.plusLuxuryHandbag" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> บวก Bank fee, Insurance , Storage</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="plusBankFee" #plusBankFee="ngModel" [(ngModel)]="dataForm.plusBankFee" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ส่วนลด </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="discount" #discount="ngModel" [(ngModel)]="dataForm.discount" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> สรุปยอดโอน </label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="transferSummary" #transferSummary="ngModel" [(ngModel)]="dataForm.transferSummary" disabled>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div style="height: 10px;"></div>
</div>
</div>
</div>
</div>
<div class="card card-table mb-6">
<div class="card-body">
<div class="table-wrap" *ngIf="dataForm.quotationDetail?.[0]">
<table class="tables">
<thead>
<tr>
<th>งวดที่</th>
<th>กำหนดจ่ายวันที่ <br>Due date</th>
<th>วันที่จ่าย <br>Payment date</th>
<th>เงินต้น <br>Principle</th>
<th>ดอกเบี้ย(บาท) <br>Interest Total</th>
<th>Bank fee, <br>Insurance ,Storage</th>
<th>ค่าชำระล่าช้า</th>
<th>รวมยอดจ่ายต่อเดือน <br>Total payment</th>
<th>เงินต้นคงเหลือ <br>Principle Total</th>
<th>ชำระเงิน</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let item of dataForm.quotationDetail; let i = index">
<tr>
<td class="text-center">{{item.installment }}</td>
<td class="text-center">{{item.dueDate | date : 'dd/MM/YYYY'}}</td>
<td class="text-center"><span *ngIf="item.status === 'paid'"> {{item.paymentDate | date : 'dd/MM/YYYY'}}</span></td>
<td class="text-center">{{item.principle | number : '1.0-0'}}</td>
<td class="text-center">{{item.interestTotal | number : '1.0-0'}}</td>
<td class="text-center">{{item.fee | number : '1.0-0'}}</td>
<td class="text-center">
<div class="b-color-red" *ngIf="item.interestLateTotal">
{{item.interestLateTotal | number : '1.2-2'}}
</div>
</td>
<td class="text-center">
<span class="b-color-green">
<ng-container *ngIf="item.totalPaymentAll"> {{ item.totalPaymentAll | number : '1.0-0'}}</ng-container>
<ng-container *ngIf="!item.totalPaymentAll"> {{ item.totalPayment | number : '1.0-0'}}</ng-container>
</span>
</td>
<td class="text-center"><span class="b-color-orange" *ngIf="item.principleTotal">{{item.principleTotal | number : '1.0-0'}}</span></td>
<td class="text-center">
<span *ngIf="item.status === 'paid'"> <i class="i bi-check-circle-fill color-green"></i></span>
</td>
</tr>
</ng-container>
<tr>
<td colspan="3" class="text-right"><b>รวม</b></td>
<td class="text-center">{{dataForm.principleSum | number : '1.0-0'}}</td>
<td class="text-center">{{dataForm.interestTotalSum | number : '1.0-0'}}</td>
<td class="text-center">{{dataForm.feeSum | number : '1.0-0'}}</td>
<td class="text-center">{{dataForm.interestLateTotalSum | number : '1.0-0'}}</td>
<td class="text-center"><span class="b-color-green">{{dataForm.totalPaymentSum | number : '1.0-0'}}</span></td>
<td class="text-center"></td>
<td class="text-center"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-table mb-6" *ngIf="quotationPayment?.[0]">
<div class="card-header"><b>ชำระเงินเพิ่มเติม</b></div>
<div class="card-body">
<div class="table-wrap">
<table class="tables">
<thead>
<tr>
<th width="60"></th>
<th width="150">วันที่จ่าย</th>
<th width="">วิธีชำระ</th>
<th>จำนวนเงิน</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let item of quotationPayment; let i = index">
<tr>
<td class="text-center">
<div class="item" *ngIf="item.status === 'paid' && item.paymentImages ">
<i class="bi bi-file-image-fill color-main" (click)="onAttachmentsView(item.paymentImages)"></i>
</div>
</td>
<td class="text-center">{{item.paymentDate | date : 'dd/MM/YYYY'}}</td>
<td class="text-center">
<div *ngIf="item.paymentMethod === 'transfer' " class="status-text status-transfer">โอนเงิน</div>
<div *ngIf="item.paymentMethod === 'cash'" class="status-text status-cash">เงินสด</div>
</td>
<td class="text-center"><span class="b-color-green">{{item.paymentAmountAll | number : '1.0-0'}}</span></td>
</tr>
</ng-container>
<tr>
<td colspan="3" class="text-right"><b>รวม</b></td>
<td class="text-center"><span class="b-color-green">{{dataForm.quotationPaymentSum | number : '1.0-0'}}</span></td>
</tr>
<tr>
<td colspan="3" class="text-right"><b>ยอดคงเหลือ</b></td>
<td class="text-center"><span class="b-color-green">{{dataForm.contractPriceSum | number : '1.0-0'}}</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-table " *ngIf="dataForm.statusPayment === 'paid'">
<div class="card-body">
<div class="no-data color-green font-bold"> ปิดยอดแล้ว</div>
</div>
</div>
<ng-container *ngIf="dataForm.statusPayment !== 'paid' ">
<div class="mt-4 mb-4">
<label class="inline-flex items-center cursor-pointer select-none ">
<input type="checkbox" name="isContractClosing" [(ngModel)]="dataForm.isContractClosing">
<span style="padding-left: 2px;">ปิดยอด</span>
</label>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6" *ngIf="dataForm.isContractClosing">
<div class="card-header ">
<div class="card-title"> ปิดยอดการผ่อนชำระ</div>
</div>
<div class="card-body form-input-list">
<div style="height: 20px;"></div>
<div class="grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> วันที่ปิดยอด</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input
matInput
name="contractClosingDate"
#contractClosingDate="ngModel"
(click)="dpkClosingDate.open()"
[(ngModel)]="dataForm.contractClosingDate"
[matDatepicker]="dpkClosingDate"
readonly
/>
<mat-datepicker-toggle [for]="dpkClosingDate" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkClosingDate></mat-datepicker>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit"></div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> เงินต้นคงเหลือ (Total)</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="contractPrice" #contractPrice="ngModel" [(ngModel)]="dataForm.contractPrice">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> ดอกเบี้ย</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="contractInterest" #contractInterest="ngModel" [(ngModel)]="dataForm.contractInterest">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div class=" grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-4 md:col-span-12 text-right md:text-left">
<label> สรุปยอดที่ต้องชำระ</label>
</div>
<div class="col-span-4 md:col-span-12">
<mat-form-field>
<input matInput appCurrencyInputMask name="contractPriceSum" #contractPriceSum="ngModel" [(ngModel)]="dataForm.contractPriceSum">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12">
<div class="unit">บาท (THB)</div>
</div>
</div>
<div style="height: 20px;"></div>
</div>
</div>
</ng-container>
</ng-container>
<ng-container *ngIf="isTabs === 3">
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> ข้อมูลธนาคารที่รับชำระเงิน</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-8 md:col-span-12 ">
<mat-label>ชื่อธนาคาร</mat-label>
<mat-form-field>
<input matInput name="productNo" #productNo="ngModel" [(ngModel)]="dataForm.contractBankName" required>
</mat-form-field>
</div>
<div class="col-span-4 md:hidden"></div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อบัญชี</mat-label>
<mat-form-field>
<input matInput name="contractAccountName" #contractAccountName="ngModel" [(ngModel)]="dataForm.contractAccountName" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>เลขที่บัญชี</mat-label>
<mat-form-field>
<input matInput name="contractAccountNumber" #contractAccountNumber="ngModel" [(ngModel)]="dataForm.contractAccountNumber" required>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="card card-form-panel card-form-panel-blue mb-6">
<div class="card-header ">
<div class="card-title"> รายละเอียดท้ายสัญญา</div>
</div>
<div class="card-body">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<div class="col-span-12 md:col-span-12 ">
<mat-form-field>
<textarea matInput [(ngModel)]="dataForm.contractDetail" name="contractDetail" #contractDetail="ngModel" placeholder="" required></textarea>
</mat-form-field>
</div>
</div>
</div>
</div>
</ng-container>
<div class="main-form-action text-right">
<button type="submit" class="btn btn-submit">บันทึก</button>
<button type="button" class="btn btn-back" (click)="onAction('back')">ยกเลิก</button>
</div>
</form>

View File

@@ -0,0 +1,222 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import {API, CONDITIONS, EAction, EText, GENDER, PREFIX, STORAGE} from "../../../../@config/app";
import { AppService } from "../../../../app.service";
import { lastValueFrom } from "rxjs";
import { BaseFormComponent } from "../../../../@common/base/base-form.component";
import { ActivatedRoute, Router } from "@angular/router";
import { IProduct } from "../../../../app.interface";
import {orderByArray, sortByProperty} from "../../../../@common/utils/OrderBy";
import deepCopy from "../../../../@common/utils/DeepCopy";
import {AttachmentsViewComponent} from "../../../@popup/attachments-view/attachments-view.component";
import {MatDialog} from "@angular/material/dialog";
@Component({
selector: "app-contract-make-do",
templateUrl: "./contract-make-do.component.html",
styleUrls: []
})
export class ContractMakeDoComponent extends BaseFormComponent implements OnInit {
override dataForm: any = {};
dataView: IProduct = {};
auth: any = {};
title = "";
api: any = API;
storage: any = STORAGE;
attachments: any = [];
equipment: any = [];
equipmentOther: any = [];
settings: any = [];
masterProductUnit: any = [];
quotationPayment: any = [];
deviation: any = 0;
isTabs: any = 1;
prefixData = PREFIX;
genderData = GENDER;
conditions = CONDITIONS;
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
public changeDetectorRef: ChangeDetectorRef,
private attachmentsView: MatDialog,
public appService: AppService
) {
super();
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.ids = params["id"];
this.action = params["action"];
this.auth = this.appService.auth();
this.dataForm.customer = {};
this.dataForm.seller = {};
if (this.ids) await this.getData();
});
}
async onAction(action: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.BACK));
if (!sweetalert.isConfirmed) return;
if (action === "back") return this.router.navigate(["/pages/contract/make/list", this.action]);
return;
}
async getData() {
if (!this.ids) this.appService.message(EAction.INFO, EText.NO_DATA);
try {
this.dataForm = await lastValueFrom(this.appService.get(`${this.api.quotation}/getById/${this.ids}`));
this.attachments = this.dataForm.images ? this.dataForm.images?.split(",") : [];
this.equipment = this.dataForm.equipment ? this.dataForm.equipment?.split(",") : [];
this.equipmentOther = this.dataForm.equipmentOther ? this.dataForm.equipmentOther?.split(",") : [];
this.dataForm.deposit = Number(this.dataForm.deposit) + Number(this.dataForm.sellerDeposit2ndTime) + Number(this.dataForm.sellerDeposit3rdTime);
this.dataForm.contractDate = new Date();
if (!this.dataForm.customerId) {
this.dataForm.customer = {};
this.dataForm.customer.prefix = this.dataForm.customerPrefix;
this.dataForm.customer.firstName = this.dataForm.customerFirstName;
this.dataForm.customer.lastName = this.dataForm.customerLastName;
this.dataForm.customer.phone = this.dataForm.customerPhone;
}
sortByProperty(this.dataForm.quotationDetail, 'installment', 'ASC');
this.dataForm.principleSum = 0
this.dataForm.interestTotalSum = 0
this.dataForm.interestLateTotalSum = 0
this.dataForm.feeSum = 0
this.dataForm.totalPaymentSum = 0
this.dataForm.contractPrincipleSum = 0
this.dataForm.contractInterestTotalSum = 0
this.dataForm.contractFeeSum = 0
this.dataForm.contractTotalPaymentSum = 0
this.dataForm.quotationDetail.map((item : any) => {
this.dataForm.principleSum += Number(item.principle)
this.dataForm.interestTotalSum += Number(item.interestTotal)
this.dataForm.interestLateTotalSum += Number(item.interestLateTotal)
this.dataForm.feeSum += Number(item.fee)
this.dataForm.totalPaymentSum += item.totalPaymentAll ? Number(item.totalPaymentAll) : Number(item.totalPayment)
if (item.status === 'pending') {
this.dataForm.contractPrincipleSum += Number(item.principle)
this.dataForm.contractInterestTotalSum += Number(item.interestTotal)
this.dataForm.contractFeeSum += Number(item.fee)
this.dataForm.contractTotalPaymentSum += item.totalPaymentAll ? Number(item.totalPaymentAll) : Number(item.totalPayment)
}
})
this.dataForm.principleSum = Math.round(this.dataForm.principleSum);
this.dataForm.contractTotalPaymentSum = Math.round(this.dataForm.contractTotalPaymentSum);
this.dataForm.contractClosingDate = new Date();
this.dataForm.contractPrice = this.dataForm.contractPrincipleSum;
this.dataForm.contractInterest = this.dataForm.contractInterestTotalSum;
this.dataForm.contractPriceSum = this.dataForm.contractTotalPaymentSum + this.dataForm.contractInterestTotalSum;
this.dataForm.contractBankName = 'ธนาคารทหารไทยธนชาต จำกัด (มหาชน)';
this.dataForm.contractAccountName = 'บริษัท ซีเอ็ม เอฟเอส จำกัด';
this.dataForm.contractAccountNumber = '263-2-17778-4';
this.dataForm.contractDetail = 'ชำระเงินงวดอย่างน้อย ทุกเดือนตามวันและยอดขั้นต่ำตามตาราง โดยที่ไม่เสียค่าปรับ ทั้งนี้หากเกินกำหนด ผู้กู้ต้องเสียค่าดอกเบี้ยผิดนัดเพิ่มเติมวันละ 1,000 บาท (ไม่รวมค่าทวงถาม) หากขาดส่งเกินกว่า 60 วัน นับแต่วันผ่อนล่าสุดจะถือว่าผิดสัญญา โดยหากผู้กู้ติดสัญญาไม่ว่ากรณีใดๆ ผู้กู้ยินดีที่จะนำสังหาริมทรัพย์ที่ผู้กู้นำเงินที่กู้ไปซื้อเป็นค่าตอบแทนในการชำระหนี้สินส่วนที่เหลือโดยทันที';
this.quotationPayment = await lastValueFrom(this.appService.get(`${API.quotationPayment}?showAll=true&status=paid&paymentType=receive&quotationId=${this.ids}`));
this.dataForm.quotationPaymentSum = 0;
this.quotationPayment.map((item : any) => {
this.dataForm.quotationPaymentSum += Number(item.paymentAmountAll)
});
if (this.dataForm.quotationPaymentSum) {
this.dataForm.contractPriceSum = this.dataForm.contractPriceSum - this.dataForm.quotationPaymentSum;
}
this.changeDetectorRef.detectChanges();
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onSubmit(form: any) {
if (!form.valid) return false;
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.CREATE));
if (!sweetalert.isConfirmed) return;
this.dataForm.pageAction = 'contractMake';
this.dataForm.images = this.attachments?.[0] ? this.attachments.join(",") : null;
this.dataForm.contractBy = this.auth.id;
this.dataForm.contractPrice = Number(this.dataForm.contractPrice);
this.dataForm.contractInterest = Number(this.dataForm.contractInterest);
this.dataForm.contractPriceSum = Number(this.dataForm.contractPriceSum);
return await this.onUpdate();
}
async onUpdate() {
try {
await lastValueFrom(this.appService.post(`${this.api.quotation}/update/${this.ids}`, this.dataForm));
await this.appService.message(EAction.SUCCESS, EText.UPDATE);
await this.router.navigate(["/pages/contract/make/list", this.action]);
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
async onAttachments($event: any, type: any) {
const file = $event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("ref", type);
formData.append("file", file);
try {
const res = await lastValueFrom(this.appService.post(`${this.api.attachments}/products`, formData));
if (!this.attachments[0]) {
this.dataForm.coverImage = res.fileName;
}
this.attachments.push(res.fileName);
console.log(this.attachments, res);
this.changeDetectorRef.detectChanges();
} catch (e) {
this.appService.message(EText.ERROR);
}
}
async onRemoveAttachments(i: number, fileName: string) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
// await lastValueFrom(this.appService.delete(`${this.api.attachments}/deleteByName`, fileName));
this.attachments?.splice(i, 1);
if (!this.attachments[0]) {
this.dataForm.coverImage = null;
}
this.changeDetectorRef.detectChanges();
}
async onAttachmentsView(images : any) {
const dialogConfig = deepCopy(this.dialogConfig);
dialogConfig.data.action = EAction.POPUP;
dialogConfig.data.title = 'ไฟล์แนบ';
dialogConfig.data.type = 'images';
dialogConfig.data.images = images;
const dialogRef = this.attachmentsView.open(AttachmentsViewComponent, dialogConfig);
const afterClosed = await lastValueFrom(dialogRef.afterClosed());
}
}

View File

@@ -0,0 +1,202 @@
<div class="card card-table">
<div class="card-filter text-right">
<div class="card-filter-section grid grid-cols-12 gap-4 md:gap-2 items-center">
<div class="col-span-5 md:col-span-12 md:order-2">
<mat-form-field>
<i matTextPrefix class="bi bi-search"></i>
<input matInput name="keyword" #keyword="ngModel" [(ngModel)]="dataFilter.keyword" (ngModelChange)="onFilter($event)">
</mat-form-field>
</div>
<div class="col-span-7 md:col-span-12 md:order-1">
<button type="button" class="btn btn-export" (click)="onExport()">Export</button>
</div>
</div>
<div class="card-filter-section grid grid-cols-12 gap-4 items-center md:gap-2 ">
<div class="col-span-6 md:col-span-12">
<div class="tabs-btn">
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'wait'}" (click)="onTabs('wait')">รอทำสัญญา</button>
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'pending'}" (click)="onTabs('pending')">รออนุมัติ</button>
<button type="button" class="btn" [ngClass]="{ 'active' : action === 'approved'}" (click)="onTabs('approved')">สัญญา</button>
</div>
</div>
<div class="col-span-6 md:col-span-12 ">
<div class="flex w-full justify-end md:justify-start">
<div class="">จำนวนทั้งหมด {{totalItem}} รายการ</div>
<div class="pl-2 pr-2">|</div>
<div class="">ค้นหาจำนวน {{totalOfElement}} รายการ</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-wrap">
<table class="table table-main" mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<ng-container matColumnDef="quotationNo">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>เลขที่ใบเสนอราคา</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.quotationNo}}</td>
</ng-container>
<ng-container matColumnDef="customerFirstName">
<th mat-header-cell *matHeaderCellDef class="" mat-sort-header>ชื่อลูกค้า</th>
<td mat-cell *matCellDef="let item" width="150" class="">
<ng-container *ngIf="item.customerId"> {{item.customer?.prefix}}{{item.customer?.firstName}} {{item.customer?.lastName}}</ng-container>
<ng-container *ngIf="!item.customerId">{{item.customerFirstName}} {{item.customerLastName}}</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="productNo">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>BOM</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.productNo}}</td>
</ng-container>
<ng-container matColumnDef="productName">
<th mat-header-cell *matHeaderCellDef class="tal">Model</th>
<td mat-cell *matCellDef="let item" class="" style="min-width: 220px;">{{item.productName }}</td>
</ng-container>
<ng-container matColumnDef="productBrandName">
<th mat-header-cell *matHeaderCellDef class="tal">Brand</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productBrandName }}</td>
</ng-container>
<ng-container matColumnDef="productSize">
<th mat-header-cell *matHeaderCellDef class="tal">Main</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productSize }}</td>
</ng-container>
<ng-container matColumnDef="productWeight">
<th mat-header-cell *matHeaderCellDef class="tal" >น้ำหนัก</th>
<td mat-cell *matCellDef="let item" class="">{{item.productWeight }}</td>
</ng-container>
<ng-container matColumnDef="productColor">
<th mat-header-cell *matHeaderCellDef class="tal">Color</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productColor }}</td>
</ng-container>
<ng-container matColumnDef="productYear">
<th mat-header-cell *matHeaderCellDef class="tal">Year</th>
<td mat-cell *matCellDef="let item" class="" >{{item.productYear }}</td>
</ng-container>
<ng-container matColumnDef="userFullName">
<th mat-header-cell *matHeaderCellDef class="tal">ชื่อคนขาย</th>
<td mat-cell *matCellDef="let item" style="min-width: 200px;" >{{item.userFullName }}</td>
</ng-container>
<ng-container matColumnDef="price">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-green"> {{item.price | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="deposit">
<th mat-header-cell *matHeaderCellDef class="tal" width="150" mat-sort-header>จำนวนมัดจำ</th>
<td mat-cell *matCellDef="let item" class="">
<div class="b-color-orange"> {{item.deposit | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="wantToInstallmentTerm">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ระยะเวลาผ่อน</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.wantToInstallmentTerm }} งวด</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่บันทึก</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.createdDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="contractApprovedDate">
<th mat-header-cell *matHeaderCellDef class="tac">วันที่อนุมัติ</th>
<td mat-cell *matCellDef="let item" class="tac">{{item.contractApprovedDate | date : 'dd/MM/YYYY'}}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef class="tac">สถานะ</th>
<td mat-cell *matCellDef="let item" class="tac" style="min-width: 100px;">
<div *ngIf="item.status === 'paid' " class="status status-active">ชำระแล้ว</div>
<div *ngIf="item.status === 'pending'" class="status status-disabled">รอชำระ</div>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ประเภทการชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.type === 'deposit' " class="status-text status-deposit">ค่ามัดจำ</div>
<div *ngIf="item.type === 'installment'" class="status-text status-installment">ผ่อนสินค้า</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentType">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>ประเภทการชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.paymentType === 'deposit' " class="status-text status-deposit">ค่ามัดจำ</div>
<div *ngIf="item.paymentType === 'installment'" class="status-text status-installment">ผ่อนสินค้า</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentMethod">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>วิธีชำระ</th>
<td mat-cell *matCellDef="let item" class="tac">
<div *ngIf="item.paymentMethod === 'transfer' " class="status-text status-transfer">โอนเงิน</div>
<div *ngIf="item.paymentMethod === 'cash'" class="status-text status-cash">เงินสด</div>
</td>
</ng-container>
<ng-container matColumnDef="paymentAmountAll">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>จำนวนเงิน</th>
<td mat-cell *matCellDef="let item" class="tac">
<div class="b-color-green">{{item.paymentAmountAll | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="sellerDeposit3rdTime">
<th mat-header-cell *matHeaderCellDef class="tac" width="150" mat-sort-header>เงินมัดจำเพิ่ม</th>
<td mat-cell *matCellDef="let item" class="tac">
<div class="b-color-red" *ngIf="item.sellerDeposit3rdTime">{{item.sellerDeposit3rdTime | number : '1.2-2'}}</div>
</td>
</ng-container>
<ng-container matColumnDef="contractBy">
<th mat-header-cell *matHeaderCellDef class="tac">พนักงานทำรายการ</th>
<td mat-cell *matCellDef="let item" class="tac">{{item?.userContractBy?.name}}</td>
</ng-container>
<ng-container matColumnDef="contractApprovedBy">
<th mat-header-cell *matHeaderCellDef class="tac">ผู้อนุมัติ</th>
<td mat-cell *matCellDef="let item" class="tac">{{item?.userApprovedBy?.name }}</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef width="80">Action</th>
<td mat-cell *matCellDef="let item">
<div class="action flex justify-center">
<div class="item" *ngIf="['wait', 'approved'].includes(item.statusContract)" >
<i class="bi bi-pencil-square icon-edit" (click)="onAction('do', item.id)"></i>
</div>
<div class="item" *ngIf="['pending', 'approved'].includes(item.statusContract)">
<i class="bi bi-filetype-pdf color-red" (click)="onAction('pdf', item.id)"></i>
</div>
<div class="item" *ngIf="['pending', 'approved'].includes(item.statusContract)">
<i class="bi bi bi-arrow-clockwise color-green" (click)="onAction('refinance', item.id)"></i>
</div>
</div>
</td>
</ng-container>
</table>
<div *ngIf="dataSource?.length === 0" class="no-data"></div>
</div>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons (page)="getData($event)"></mat-paginator>
</div>
</div>

View File

@@ -0,0 +1,131 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import { BaseListComponent } from "../../../../@common/base/base-list.component";
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from "rxjs";
import { MatDialog } from "@angular/material/dialog";
import { AppService } from "../../../../app.service";
import { API, EAction, EStatusContract, EStatusQuotation, EText } from "../../../../@config/app";
import { ActivatedRoute, Router } from "@angular/router";
import generateParamsValue from "../../../../@common/utils/GenerateParamsValue";
@Component({
selector: "app-appraisal-3rd-time-index",
templateUrl: "./contract-make-index.component.html",
styleUrls: []
})
export class ContractMakeIndexComponent extends BaseListComponent implements OnInit {
pageTitle = "สัญญา";
action = "pending";
apiUrl: string = API.quotation;
api: any = API;
displayedColumns: string[] = [];
masterProductCategory: any = [];
masterProductBrand: any = [];
filterKeyword: Subject<string> = new Subject<string>();
constructor(
private dialog: MatDialog,
private router: Router,
public appService: AppService,
public activatedRoute: ActivatedRoute,
public changeDetectorRef: ChangeDetectorRef
) {
super();
this.filterKeyword.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(model => {
this.getData();
});
}
async ngOnInit() {
this.activatedRoute.params.subscribe(async params => {
this.action = params["action"];
if (!this.action) this.router.navigate(["/pages/contract/make/list", EStatusQuotation.WAIT]);
await this.getData();
});
}
async onTabs(action?: any) {
this.dataFilter = {};
return this.router.navigate(["/pages/contract/make/list", action]);
}
async onAction(action : any, id?: any) {
if (action === 'do') return this.router.navigate([`/pages/contract/make/do/${this.action}`, id]);
if (action === 'pdf') return this.router.navigate([`/pages/contract/make/pdf/${this.action}`, id]);
if (action === 'refinance') {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.REFINANCE));
if (!sweetalert.isConfirmed) return;
}
return;
}
async getData($event?: any) {
try {
this.dataSource = [];
this.dataFilter.step = 5;
this.dataFilter.statusContract = this.action;
let url = API.quotation;
if (this.action === EStatusContract.WAIT) {
url = API.quotation;
this.displayedColumns = ["action", "price", "wantToInstallmentTerm", "customerFirstName", "productNo", "productName", "productBrandName", "productSize", "productWeight", "productColor", "productYear", "contractBy", "userFullName"];
}
if (this.action === EStatusContract.PENDING) {
url = API.quotation;
this.displayedColumns = ["action", "price", "wantToInstallmentTerm", "customerFirstName", "productNo", "productName", "productBrandName", "productSize", "productWeight", "productColor", "contractBy", "createdDate"];
}
if (this.action === EStatusContract.APPROVED) {
url = API.quotation;
this.displayedColumns = ["action", "price", "wantToInstallmentTerm", "customerFirstName", "productNo", "productName", "productBrandName", "productSize", "productWeight", "productColor", "contractBy", "contractApprovedBy", "contractApprovedDate"];
}
this.dataFilter.keywordColumn = "quotationNo,productNo,customerFirstName,price";
const dataSource = await lastValueFrom(this.appService.get(this.setParams(url, $event)));
this.dataSource = this.setDataSource<any>(dataSource);
} catch (e) {
this.dataSource = [];
}
}
onFilter($event?: any) {
this.filterKeyword.next($event);
}
clearDate($event?: any) {
$event.stopPropagation();
this.dataFilter.createdDate = null;
}
async onSort($event: any) {
this.dataFilter.orderBy = $event.active;
this.dataFilter.sort = $event.direction;
await this.getData();
console.log($event);
}
async onDelete(ids: any) {
const sweetalert = await lastValueFrom(this.appService.confirm(EAction.DELETE));
if (!sweetalert.isConfirmed) return;
try {
await lastValueFrom(this.appService.delete(this.apiUrl, ids));
await this.appService.message(EAction.SUCCESS, EText.DELETE);
await this.getData(this.getCurrentPage());
} catch (err) {
this.appService.message(EAction.ERROR, EText.ERROR);
}
}
onExport() {
const filter = generateParamsValue(this.dataFilter);
const url = `${API.quotation}/export-contract?${filter ? '&' + filter : '' }`;
window.open(url);
}
}

View File

@@ -0,0 +1,3 @@
<mat-progress-bar *ngIf="!pdfView" mode="indeterminate"></mat-progress-bar>
<iframe *ngIf="pdfView" [src]="pdfView"></iframe>

Some files were not shown because too many files have changed in this diff Show More