[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

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>

View File

@@ -0,0 +1,111 @@
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-make-pdf.component.html",
styleUrls: []
})
export class ContractMakePdfComponent 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,
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,14 @@
import {NgModule} from '@angular/core';
import {AppSharedModule} from '../../app.shared';
import {Components, DashboardRoutingModule} from './dashboard.routing.module';
@NgModule({
declarations: [...Components],
imports: [
AppSharedModule,
DashboardRoutingModule,
]
})
export class DashboardModule {
}

View File

@@ -0,0 +1,22 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {IndexComponent} from './index/index.component';
const routes: Routes = [
{path: '', component: IndexComponent},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DashboardRoutingModule { }
export const Components = [
IndexComponent,
]

View File

@@ -0,0 +1,4 @@
<section class="section section-content">
</section>

View File

@@ -0,0 +1,40 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { AppService } from "../../../app.service";
import { lastValueFrom } from "rxjs";
import { API } from "../../../@config/app";
@Component({
selector: "app-index",
templateUrl: "./index.component.html",
styleUrls: []
})
export class IndexComponent implements OnInit {
title = "";
auth : any = {};
constructor(public router: Router,
private route: ActivatedRoute,
private app: AppService) {
}
ngOnInit() {
this.auth = this.app.auth();
if (!this.auth) return;
if (this.auth.userType === "ADMIN") {
return this.router.navigate(["/pages/appraisal/1st-time"]);
}
return ;
}
}

View File

@@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { IndexComponent } from './index/index.component';
const routes: Routes = [
{path: '', component: IndexComponent},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ErrorsRoutingModule { }

View File

@@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { AppSharedModule } from '../../app.shared';
import { IndexComponent } from './index/index.component';
import { ErrorsRoutingModule } from './errors-routing.module';
@NgModule({
declarations: [IndexComponent],
imports: [AppSharedModule, ErrorsRoutingModule],
})
export class ErrorsModule {}

View File

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

View File

@@ -0,0 +1,217 @@
<form class="main-form" #ngf="ngForm" (ngSubmit)="onSubmit(ngf)">
<div class="grid grid-cols-12 gap-4 md:gap-2 mt-4 mb-4">
<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="quotationNo" #quotationNo="ngModel" [(ngModel)]="dataForm.quotationNo">
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>วันที่ทำรายการ</mat-label>
<mat-form-field>
<input
matInput
name="sellerPaymentDate"
#sellerPaymentDate="ngModel"
(click)="dpkName3.open()"
[(ngModel)]="dataForm.sellerPaymentDate"
[matDatepicker]="dpkName3"
readonly
/>
<mat-datepicker-toggle [for]="dpkName3" matSuffix></mat-datepicker-toggle>
<mat-datepicker #dpkName3></mat-datepicker>
</mat-form-field>
</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="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>
<input matInput name="sellerprefix" #sellerprefix="ngModel" [(ngModel)]="dataForm.seller.prefix" disabled>
</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" disabled>
</mat-form-field>
</div>
<div class="col-span-2 md:col-span-12 ">
<mat-label>เพศ</mat-label>
<input matInput name="sellergender" #sellergender="ngModel" [(ngModel)]="dataForm.seller.gender" disabled>
</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">
</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>
<div class="card card-table mb-6">
<div class="card-body">
<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.sellerDeposit | 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"> ข้อมูลการชำระเงิน</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="sellerPaymentMethod" #sellerPaymentMethod="ngModel" [(ngModel)]="dataForm.sellerPaymentMethod" appendTo="body" required>
<ng-option *ngFor="let item of paymentMethods" [value]="item.value">{{item.name}}</ng-option>
</ng-select>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ผู้รับเงิน</mat-label>
<mat-form-field>
<input matInput name="sellerPaymentPayee" #sellerPaymentPayee="ngModel" [(ngModel)]="dataForm.sellerPaymentPayee" 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="sellerPaymentAccountNumber" #sellerPaymentAccountNumber="ngModel" [(ngModel)]="dataForm.sellerPaymentAccountNumber" required>
</mat-form-field>
</div>
<div class="col-span-4 md:col-span-12 ">
<mat-label>ชื่อธนาคาร</mat-label>
<mat-form-field>
<input matInput name="sellerPaymentAccountName" #sellerPaymentAccountName="ngModel" [(ngModel)]="dataForm.sellerPaymentAccountName" 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 class="!text-right" appCurrencyInputMask matInput name="sellerPaymentAmount" #sellerPaymentAmount="ngModel" [(ngModel)]="dataForm.sellerPaymentAmount" required>
</mat-form-field>
</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,137 @@
import { ChangeDetectorRef, Component, OnInit } from "@angular/core";
import { API, EAction, EText, 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";
@Component({
selector: "app-finance-invoice-do",
templateUrl: "./finance-invoice-do.component.html",
styleUrls: []
})
export class FinanceInvoiceDoComponent 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;
isTabs: any = 1;
paymentMethods = [
{value : 'transfer', name : 'โอนเงิน'},
{value : 'cash', name : 'เงินสด'},
]
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.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/finance/invoice/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.dataForm.sellerPaymentDate = new Date();
this.dataForm.sellerPaymentAmount = Number(this.dataForm.price) - Number(this.dataForm.sellerDeposit);
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.images = this.attachments?.[0] ? this.attachments.join(",") : null;
this.dataForm.pageAction = 'financeInvoice';
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/finance/invoice/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();
}
}

View File

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

View File

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

View File

@@ -0,0 +1,200 @@
<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 === 'invoice'}" (click)="onTabs('invoice')">ตั้งหนี้แล้ว</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="sellerName">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>ชื่อคนขาย</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item?.seller?.name}}</td>
</ng-container>
<ng-container matColumnDef="updatedDate">
<th mat-header-cell *matHeaderCellDef class="tac" mat-sort-header>วันที่ทำรายการ</th>
<td mat-cell *matCellDef="let item" width="150" class="">{{item.updatedDate | date : 'dd/MM/YYYY'}}</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="sellerPaymentAmount">
<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.sellerPaymentAmount | 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="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.isInvoice === 'pending' " >
<i class="bi bi-pencil-square icon-edit" (click)="onAction('do', 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,124 @@
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-finance-invoice-index",
templateUrl: "./finance-invoice-index.component.html",
styleUrls: []
})
export class FinanceInvoiceIndexComponent 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/finance/invoice/list", 'pending']);
await this.getData();
});
}
async onTabs(action?: any) {
this.dataFilter = {};
return this.router.navigate(["/pages/finance/invoice/list", action]);
}
onAction(action : any, id?: any) {
if (action === 'do') return this.router.navigate([`/pages/finance/invoice/do/${this.action}`, id]);
if (action === 'pdf') return this.router.navigate([`/pages/finance/invoice/pdf/${this.action}`, id]);
return;
}
async getData($event?: any) {
try {
this.dataSource = [];
this.dataFilter.step = 5;
let url = API.quotation;
if (this.action === 'pending') {
this.dataFilter.isInvoice = 'pending';
this.dataFilter.keywordColumn = "quotationNo,productNo,customerFirstName,customerLastName,price";
this.displayedColumns = ["action", "price", "sellerName", "productNo", "productName", "customerFirstName", "updatedDate"];
}
if (this.action === 'invoice') {
this.dataFilter.isInvoice = 'invoice';
this.dataFilter.keywordColumn = "quotationNo,productNo,customerFirstName,customerLastName,sellerPaymentAmount";
this.displayedColumns = [ "sellerPaymentAmount", "sellerName", "productNo", "productName", "customerFirstName", "updatedDate"];
}
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-invoice?${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