print and report
This commit is contained in:
@@ -45,37 +45,6 @@ export class MainDashboardComponent implements OnInit {
|
|||||||
private dashboardStateService: DashboardStateService
|
private dashboardStateService: DashboardStateService
|
||||||
){}
|
){}
|
||||||
|
|
||||||
readonly kpiCards = [
|
|
||||||
{
|
|
||||||
label: 'รายรับรวม',
|
|
||||||
value: '฿1.28M',
|
|
||||||
trend: '+12.4%',
|
|
||||||
context: 'เทียบกับเดือนก่อน',
|
|
||||||
accent: 'mint'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'รายจ่ายรวม',
|
|
||||||
value: '฿732K',
|
|
||||||
trend: '-4.1%',
|
|
||||||
context: 'จัดการได้ดีขึ้น',
|
|
||||||
accent: 'lavender'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'ยอดค้างชำระ',
|
|
||||||
value: '฿184K',
|
|
||||||
trend: '-2 ใบแจ้งหนี้',
|
|
||||||
context: 'รอติดตาม',
|
|
||||||
accent: 'amber'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'อัตรากำไร',
|
|
||||||
value: '37.8%',
|
|
||||||
trend: '+1.9 จุด',
|
|
||||||
context: 'ระยะ 30 วัน',
|
|
||||||
accent: 'teal'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// readonly revenueTrend = [
|
// readonly revenueTrend = [
|
||||||
// { label: 'ม.ค.', value: 52 },
|
// { label: 'ม.ค.', value: 52 },
|
||||||
// { label: 'ก.พ.', value: 61 },
|
// { label: 'ก.พ.', value: 61 },
|
||||||
@@ -98,71 +67,8 @@ export class MainDashboardComponent implements OnInit {
|
|||||||
isNumber(val: any): boolean {
|
isNumber(val: any): boolean {
|
||||||
return typeof val === 'number';
|
return typeof val === 'number';
|
||||||
}
|
}
|
||||||
readonly periodSummaries = [
|
|
||||||
{
|
|
||||||
label: 'รายปี',
|
|
||||||
note: 'ปี 2567',
|
|
||||||
income: '฿9.6M',
|
|
||||||
expense: '฿5.1M',
|
|
||||||
net: '+฿4.5M',
|
|
||||||
trend: '+18%',
|
|
||||||
badge: 'year'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'รายเดือน',
|
|
||||||
note: 'มิถุนายน 2567',
|
|
||||||
income: '฿1.28M',
|
|
||||||
expense: '฿732K',
|
|
||||||
net: '+฿548K',
|
|
||||||
trend: '+6%',
|
|
||||||
badge: 'month'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'รายสัปดาห์',
|
|
||||||
note: 'สัปดาห์ที่ 24',
|
|
||||||
income: '฿312K',
|
|
||||||
expense: '฿188K',
|
|
||||||
net: '+฿124K',
|
|
||||||
trend: '+2%',
|
|
||||||
badge: 'week'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
readonly alerts = [
|
|
||||||
{
|
|
||||||
title: 'ใบแจ้งหนี้ #INV-083 จะครบกำหนด',
|
|
||||||
detail: 'ลูกค้า Metro Engineering',
|
|
||||||
tag: 'ภายใน 3 วัน'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'มีเอกสารที่ต้องอนุมัติ 2 รายการ',
|
|
||||||
detail: 'เบิกค่าใช้จ่ายฝ่ายการตลาด',
|
|
||||||
tag: 'รออนุมัติ'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'พบรายการใช้จ่ายผิดปกติ',
|
|
||||||
detail: 'ค่าใช้จ่ายเดินทางสูงกว่าค่าเฉลี่ย 28%',
|
|
||||||
tag: 'ตรวจสอบ'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
readonly tasks = [
|
|
||||||
{
|
|
||||||
title: 'กระทบยอดธนาคาร เดือน มิ.ย.',
|
|
||||||
due: 'วันนี้ 16:00',
|
|
||||||
priority: 'สูง'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'เตรียมรายงาน VAT',
|
|
||||||
due: 'พรุ่งนี้ 10:30',
|
|
||||||
priority: 'กลาง'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'ออกใบเสนอราคา โครงการใหม่',
|
|
||||||
due: 'ศุกร์ 14:00',
|
|
||||||
priority: 'ต่ำ'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// readonly ledgerEntries = [
|
// readonly ledgerEntries = [
|
||||||
// {
|
// {
|
||||||
@@ -199,14 +105,6 @@ isNumber(val: any): boolean {
|
|||||||
// }
|
// }
|
||||||
// ];
|
// ];
|
||||||
|
|
||||||
readonly expenseBreakdown = [
|
|
||||||
{ label: 'ฝ่ายบริหาร', value: 32, color: '#0ea5e9' },
|
|
||||||
{ label: 'การตลาด', value: 18, color: '#f97316' },
|
|
||||||
{ label: 'ต้นทุนโครงการ', value: 27, color: '#10b981' },
|
|
||||||
{ label: 'บุคลากร', value: 15, color: '#a855f7' },
|
|
||||||
{ label: 'อื่นๆ', value: 8, color: '#e11d48' }
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.setupFormControl();
|
this.setupFormControl();
|
||||||
|
|||||||
@@ -209,80 +209,127 @@
|
|||||||
color: #dc2626;
|
color: #dc2626;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- UPDATED PIE CHART SECTION (Donut Style) --- */
|
||||||
|
|
||||||
.pie-panel__content {
|
.pie-panel__content {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1.5rem;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 3rem; /* ระยะห่างระหว่างกราฟกับ Legend */
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pie-chart {
|
||||||
|
width: 180px;
|
||||||
|
height: 180px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
/* Flex เพื่อจัดรูตรงกลาง */
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pie-chart {
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
border-radius: 50%;
|
|
||||||
position: relative;
|
|
||||||
box-shadow: inset 0 0 20px rgba(15, 23, 42, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pie-chart__center {
|
.pie-chart__center {
|
||||||
position: absolute;
|
width: 75%; /* ความหนาของขอบ */
|
||||||
top: 50%;
|
height: 75%;
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 110px;
|
|
||||||
height: 110px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.1);
|
box-shadow: inset 0 2px 10px rgba(0,0,0,0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pie-chart__center p {
|
.chart-shadow {
|
||||||
|
position: absolute;
|
||||||
|
width: 160px;
|
||||||
|
height: 160px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(0,0,0,0.03);
|
||||||
|
filter: blur(20px);
|
||||||
|
z-index: 1;
|
||||||
|
top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-muted {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #94a3b8;
|
font-size: 0.8rem;
|
||||||
font-size: 0.85rem;
|
color: #9ca3af;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pie-chart__center strong {
|
.total-amount {
|
||||||
color: #0f172a;
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1f2937;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Legend Styles */
|
||||||
.pie-legend {
|
.pie-legend {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.8rem;
|
gap: 1.5rem; /* ระยะห่างแต่ละ item */
|
||||||
|
min-width: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pie-legend li {
|
.pie-legend__item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
gap: 0.6rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swatch {
|
.swatch {
|
||||||
width: 14px;
|
width: 12px;
|
||||||
height: 14px;
|
height: 12px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
margin-top: 6px; /* ดันลงมาให้ตรงกับ Text บรรทัดแรก */
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend-label {
|
.legend-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #374151;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-percent {
|
||||||
|
font-size: 0.9rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend-value {
|
.item-value {
|
||||||
margin: 0;
|
|
||||||
color: #94a3b8;
|
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
color: #6b7280;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- END UPDATED PIE CHART SECTION --- */
|
||||||
|
|
||||||
|
|
||||||
.preview-modal {
|
.preview-modal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
@@ -422,6 +469,7 @@
|
|||||||
|
|
||||||
.pie-panel__content {
|
.pie-panel__content {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,59 @@
|
|||||||
<section class="report">
|
<div class="report">
|
||||||
<header class="report__header">
|
<div class="report__header">
|
||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">สรุปรายงาน</p>
|
<p class="eyebrow">สรุปรายงาน</p>
|
||||||
<h1>รายงานรายรับรายจ่าย</h1>
|
<h1>รายงานรายรับรายจ่าย</h1>
|
||||||
<p class="muted">ช่วงวันที่ {{ reportRange.start }} - {{ reportRange.end }}</p>
|
<p class="muted">ช่วงวันที่ {{ reportRange.start }} - {{ reportRange.end }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="report__actions">
|
<div class="report__actions">
|
||||||
<button class="btn btn--ghost">ส่งออกเป็น Excel</button>
|
<!-- <button class="btn btn--ghost">ส่งออกเป็น Excel</button> -->
|
||||||
<button class="btn btn--primary" (click)="openPreview()">ปริ้นรายงาน</button>
|
<button class="btn btn--primary" (click)="openPreview()">ปริ้นรายงาน</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</div>
|
||||||
|
|
||||||
<section class="summary-grid">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||||
<article class="summary-card" *ngFor="let card of summaryCards">
|
|
||||||
<p class="summary-card__label">{{ card.label }}</p>
|
|
||||||
<h2>{{ card.value }}</h2>
|
|
||||||
<p class="summary-card__detail">{{ card.detail }}</p>
|
|
||||||
<span class="summary-card__tone" [ngClass]="'tone-' + card.tone"></span>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="report__content">
|
<div class="p-6 rounded-2xl bg-green-50 border border-green-100 shadow-sm hover:shadow-md transition duration-200">
|
||||||
<article class="panel">
|
<p class="text-sm font-medium text-green-800 opacity-70">รายรับรวม</p>
|
||||||
|
<h2 class="text-3xl font-bold text-gray-800 mt-2">{{ myActSumData.summary.totalIncome | number:'1.2-2' }}</h2>
|
||||||
|
<div class="mt-2 flex items-center text-sm font-medium text-green-600">
|
||||||
|
<!-- <span>+12.4% MoM</span> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 rounded-2xl bg-yellow-50 border border-yellow-100 shadow-sm hover:shadow-md transition duration-200">
|
||||||
|
<p class="text-sm font-medium text-yellow-800 opacity-70">รายจ่ายรวม</p>
|
||||||
|
<h2 class="text-3xl font-bold text-gray-800 mt-2">{{ myActSumData.summary.totalExpense | number:'1.2-2' }}</h2>
|
||||||
|
<div class="mt-2 flex items-center text-sm font-medium text-yellow-600">
|
||||||
|
<!-- <span>-4.1% MoM</span> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 rounded-2xl bg-purple-50 border border-purple-100 shadow-sm hover:shadow-md transition duration-200">
|
||||||
|
<p class="text-sm font-medium text-purple-800 opacity-70">กำไรสุทธิ</p>
|
||||||
|
<h2 class="text-3xl font-bold text-gray-800 mt-2">{{ myActSumData.summary.netProfit | number:'1.2-2' }}</h2>
|
||||||
|
<div class="mt-2 flex items-center text-sm font-medium text-purple-600">
|
||||||
|
<span>Margin {{ myActSumData.summary.profitRate }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 rounded-2xl bg-gray-50 border border-gray-100 shadow-sm hover:shadow-md transition duration-200">
|
||||||
|
<p class="text-sm font-medium text-gray-600 opacity-70">บันทึกรายการ</p>
|
||||||
|
<h2 class="text-3xl font-bold text-gray-800 mt-2">{{myActData.length}} รายการ</h2>
|
||||||
|
<div class="mt-2 flex items-center text-xs font-medium text-gray-500">
|
||||||
|
<!-- <span>32 รายรับ · 54 รายจ่าย</span> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="report__content">
|
||||||
|
<div class="panel">
|
||||||
<div class="panel__header">
|
<div class="panel__header">
|
||||||
<div>
|
<div>
|
||||||
<h2>สมุดรายวัน</h2>
|
<h2>สมุดรายวัน</h2>
|
||||||
<p>บันทึกรายรับรายจ่ายทั้งหมดในช่วงเวลา</p>
|
<p>บันทึกรายรับรายจ่ายทั้งหมดในช่วงเวลา</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn--compact btn--ghost">กรองข้อมูล</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<div class="table__head">
|
<div class="table__head">
|
||||||
@@ -37,106 +63,123 @@
|
|||||||
<span>หมวดหมู่</span>
|
<span>หมวดหมู่</span>
|
||||||
<span class="amount-col">ยอดเงิน</span>
|
<span class="amount-col">ยอดเงิน</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="table__row" *ngFor="let record of formattedRecords">
|
@for (idx of myActData; track idx.actseq) {
|
||||||
<span>{{ record.date }}</span>
|
<div class="table__row">
|
||||||
<span class="mono">{{ record.doc }}</span>
|
<span>{{ idx.actacpdtm | date:'dd/MM/yyyy' }}</span>
|
||||||
|
<span class="mono">RCPT{{ idx.actseq }}</span>
|
||||||
<span>
|
<span>
|
||||||
<strong>{{ record.topic }}</strong>
|
<strong>{{ idx.actcatnam }}</strong>
|
||||||
<small class="muted">{{ record.type === 'income' ? 'รายรับ' : 'รายจ่าย' }}</small>
|
<small class="muted">{{ idx.acttyp === 'i' ? 'รับ' : 'จ่าย' }}</small>
|
||||||
</span>
|
</span>
|
||||||
<span>{{ record.category }}</span>
|
<span>{{ idx.actcatnam }}</span>
|
||||||
<span class="amount-col" [ngClass]="record.tone">{{ record.displayAmount }}</span>
|
<span class="amount-col" [ngClass]="idx.acttyp === 'i' ? 'income' : 'expense' ">{{ idx.actqty | number:'1.2-2' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
} @empty {
|
||||||
|
<div class="p-4 text-center text-gray-400">ไม่มีข้อมูลรายการ</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</div>
|
||||||
|
|
||||||
<article class="panel pie-panel">
|
<div class="panel pie-panel">
|
||||||
<div class="panel__header">
|
<div class="panel__header">
|
||||||
<div>
|
<div>
|
||||||
<h2>สัดส่วนค่าใช้จ่าย</h2>
|
<h2>สัดส่วนค่าใช้จ่าย</h2>
|
||||||
<p>เปรียบเทียบหมวดหลักของรายจ่ายเดือนนี้</p>
|
<p>เปรียบเทียบหมวดหลักของรายจ่ายเดือนนี้</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="pie-panel__content">
|
|
||||||
<div class="pie-chart" [style.background]="expenseGradient">
|
|
||||||
<div class="pie-chart__center">
|
|
||||||
<p>รวมรายจ่าย</p>
|
|
||||||
<strong>฿732K</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ul class="pie-legend">
|
|
||||||
<li *ngFor="let part of expenseBreakdown">
|
|
||||||
<span class="swatch" [style.background]="part.color"></span>
|
|
||||||
<div>
|
|
||||||
<p class="legend-label">{{ part.label }}</p>
|
|
||||||
<p class="legend-value">{{ part.value }}%</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="preview-modal" *ngIf="printPreviewOpen">
|
|
||||||
<div class="preview-modal__backdrop" (click)="closePreview()"></div>
|
|
||||||
<div class="preview-modal__content">
|
|
||||||
<header class="preview-modal__header">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Print Preview</p>
|
|
||||||
<h2>รายงานรายรับรายจ่าย</h2>
|
|
||||||
<p class="muted">ช่วงวันที่ {{ reportRange.start }} - {{ reportRange.end }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="preview-modal__actions">
|
|
||||||
<button class="btn btn--ghost" (click)="closePreview()">ปิด</button>
|
|
||||||
<button class="btn btn--primary">พิมพ์</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="preview-sheet">
|
|
||||||
<div class="preview-sheet__header">
|
|
||||||
<div>
|
|
||||||
<h3>Accounting Summary</h3>
|
|
||||||
<p>Prepared on {{ reportRange.end }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="preview-totals">
|
|
||||||
<div *ngFor="let total of previewTotals">
|
|
||||||
<p>{{ total.label }}</p>
|
|
||||||
<strong>{{ total.value }}</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="preview-pie">
|
|
||||||
<div class="mini-pie" [style.background]="expenseGradient"></div>
|
|
||||||
<ul>
|
|
||||||
<li *ngFor="let part of expenseBreakdown">
|
|
||||||
<span class="swatch" [style.background]="part.color"></span>
|
|
||||||
<span>{{ part.label }} · {{ part.value }}%</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="preview-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>วันที่</th>
|
|
||||||
<th>เลขที่</th>
|
|
||||||
<th>หัวข้อ</th>
|
|
||||||
<th>หมวดหมู่</th>
|
|
||||||
<th>ยอดเงิน</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr *ngFor="let record of formattedRecords">
|
|
||||||
<td>{{ record.date }}</td>
|
|
||||||
<td>{{ record.doc }}</td>
|
|
||||||
<td>{{ record.topic }}</td>
|
|
||||||
<td>{{ record.category }}</td>
|
|
||||||
<td [ngClass]="record.tone">{{ record.displayAmount }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
<div class="pie-panel__content">
|
||||||
|
<div class="chart-wrapper">
|
||||||
|
<div class="pie-chart" [style.background]="ActSumDataGradient">
|
||||||
|
<div class="pie-chart__center">
|
||||||
|
<p class="label-muted">รวมรายจ่าย</p>
|
||||||
|
<strong class="total-amount">{{ myActSumData.summary.totalExpense | number:'1.0-0' }}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-shadow"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="pie-legend">
|
||||||
|
@for (part of myActSumData.pie.expense; track part.label) {
|
||||||
|
<li class="pie-legend__item">
|
||||||
|
<span class="swatch" [style.background]="part.color"></span>
|
||||||
|
<div class="legend-text">
|
||||||
|
<p class="item-label">{{ part.label }}</p>
|
||||||
|
<p class="item-percent">{{ part.percent }}%</p>
|
||||||
|
<p class="item-value">{{ part.value | number:'1.0-0' }} บาท</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PRINT PREVIEW MODAL -->
|
||||||
|
@if(printPreviewOpen){
|
||||||
|
<div class="preview-modal">
|
||||||
|
<div class="preview-modal__backdrop" (click)="closePreview()"></div>
|
||||||
|
<div class="preview-modal__content">
|
||||||
|
<div class="preview-modal__header">
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Print Preview</p>
|
||||||
|
<h2>รายงานรายรับรายจ่าย</h2>
|
||||||
|
</div>
|
||||||
|
<div class="preview-modal__actions">
|
||||||
|
<button class="btn btn--ghost" (click)="closePreview()">ปิด</button>
|
||||||
|
<!-- ACTION ADDED HERE -->
|
||||||
|
<button class="btn btn--primary" (click)="printReport()">พิมพ์</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preview-sheet">
|
||||||
|
<div class="preview-sheet__header">
|
||||||
|
<div>
|
||||||
|
<h3>Accounting Summary</h3>
|
||||||
|
<p>Prepared on {{ reportRange.end }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preview-pie">
|
||||||
|
<div class="mini-pie" [style.background]="ActSumDataGradient"></div>
|
||||||
|
<ul>
|
||||||
|
@for (part of myActSumData.pie.expense; track part.label) {
|
||||||
|
<li>
|
||||||
|
<span class="swatch" [style.background]="part.color"></span>
|
||||||
|
<span>{{ part.label }} · {{ part.percent }}%</span>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="preview-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>วันที่</th>
|
||||||
|
<th>เลขที่</th>
|
||||||
|
<th>หัวข้อ</th>
|
||||||
|
<th>หมวดหมู่</th>
|
||||||
|
<th>ยอดเงิน</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (idx of myActData; track idx.actseq) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ idx.actacpdtm | date:'dd/MM/yyyy'}}</td>
|
||||||
|
<td>RCPT{{ idx.actseq }}</td>
|
||||||
|
<td>{{ idx.actcatnam }}</td>
|
||||||
|
<td>{{ idx.actcatnam }}</td>
|
||||||
|
<td [ngClass]="idx.acttyp === 'i' ? 'income' : 'expense' ">{{ idx.actqty | number:'1.2-2' }}</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- The rest of the dashboard HTML is excluded for brevity/focus on the core report logic -->
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { GeneralService } from './../../services/generalservice';
|
||||||
|
import { QuickRatio, IStateDrop, IActSumData } from './../../interfaces/dashboard.interface';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { IActData } from '../../interfaces/dashboard.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-main-report',
|
selector: 'app-main-report',
|
||||||
@@ -6,7 +10,36 @@ import { Component } from '@angular/core';
|
|||||||
standalone: false,
|
standalone: false,
|
||||||
styleUrls: ['./main-report.component.css']
|
styleUrls: ['./main-report.component.css']
|
||||||
})
|
})
|
||||||
export class MainReportComponent {
|
export class MainReportComponent implements OnInit{
|
||||||
|
|
||||||
|
|
||||||
|
myActData: IActData[] = [];
|
||||||
|
quickRatios: QuickRatio[] = [];
|
||||||
|
// myDropAct: IStateDrop[] = [];
|
||||||
|
myDropAct: IStateDrop = { income: [], expense: [] };
|
||||||
|
myActSumData: IActSumData = {
|
||||||
|
summary: {
|
||||||
|
totalIncome: '',
|
||||||
|
totalExpense: '',
|
||||||
|
netProfit: 0,
|
||||||
|
profitRate: '',
|
||||||
|
adjustedProfitRate: '',
|
||||||
|
period: ''
|
||||||
|
},
|
||||||
|
pie: {
|
||||||
|
income: [],
|
||||||
|
expense: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ActSumDataGradient: any
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private generalService: GeneralService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router
|
||||||
|
){}
|
||||||
readonly reportRange = {
|
readonly reportRange = {
|
||||||
start: '1 มิถุนายน 2567',
|
start: '1 มิถุนายน 2567',
|
||||||
end: '30 มิถุนายน 2567'
|
end: '30 มิถุนายน 2567'
|
||||||
@@ -102,6 +135,60 @@ export class MainReportComponent {
|
|||||||
|
|
||||||
printPreviewOpen = false;
|
printPreviewOpen = false;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.OnSearchSum({}, false);
|
||||||
|
this.OnSearchAct({});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OnSearchAct(value: any): void {
|
||||||
|
const uri = '/api/web/accountingSearch';
|
||||||
|
let request = {
|
||||||
|
token: value
|
||||||
|
}
|
||||||
|
this.generalService.postRequest(uri, request).subscribe({
|
||||||
|
next: (result: any) => {
|
||||||
|
if (result.code === '200') {
|
||||||
|
this.generalService.trowApi(result);
|
||||||
|
this.myActData = result.data;
|
||||||
|
}else{
|
||||||
|
this.generalService.trowApi(result);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (error: any) => {
|
||||||
|
this.generalService.trowApi(error);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OnSearchSum(value: any, setupFirst: boolean): void {
|
||||||
|
const uri = '/api/web/accountingSum';
|
||||||
|
let request = {
|
||||||
|
token: value
|
||||||
|
}
|
||||||
|
this.generalService.postRequest(uri, request).subscribe({
|
||||||
|
next: (result: any) => {
|
||||||
|
if (result.code === '200') {
|
||||||
|
this.generalService.trowApi(result);
|
||||||
|
this.myActSumData = result.data
|
||||||
|
this.ActSumDataGradient = this.buildExpenseGradient();
|
||||||
|
}else{
|
||||||
|
this.generalService.trowApi(result);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (error: any) => {
|
||||||
|
this.generalService.trowApi(error);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
get expenseGradient(): string {
|
get expenseGradient(): string {
|
||||||
let current = 0;
|
let current = 0;
|
||||||
const segments = this.expenseBreakdown
|
const segments = this.expenseBreakdown
|
||||||
@@ -115,6 +202,24 @@ export class MainReportComponent {
|
|||||||
return `conic-gradient(${segments})`;
|
return `conic-gradient(${segments})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private buildExpenseGradient(): string {
|
||||||
|
if (!this.myActSumData?.pie?.expense?.length) return '';
|
||||||
|
|
||||||
|
let current = 0;
|
||||||
|
const segments = this.myActSumData.pie.expense
|
||||||
|
.map(part => {
|
||||||
|
const start = current;
|
||||||
|
const percent = parseFloat(part.percent); // แปลงจาก string → number
|
||||||
|
const end = current + percent;
|
||||||
|
current = end;
|
||||||
|
return `${part.color} ${start}% ${end}%`;
|
||||||
|
})
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
return `conic-gradient(${segments})`;
|
||||||
|
}
|
||||||
|
|
||||||
get formattedRecords() {
|
get formattedRecords() {
|
||||||
return this.ledgerRecords.map(record => ({
|
return this.ledgerRecords.map(record => ({
|
||||||
...record,
|
...record,
|
||||||
@@ -123,6 +228,10 @@ export class MainReportComponent {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printReport(): void {
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
|
||||||
openPreview(): void {
|
openPreview(): void {
|
||||||
this.printPreviewOpen = true;
|
this.printPreviewOpen = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ export interface QuickRatio {
|
|||||||
value: string | number;
|
value: string | number;
|
||||||
colorClass: string; // ตัวเก็บชื่อ class สี
|
colorClass: string; // ตัวเก็บชื่อ class สี
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export
|
||||||
// ข้อมูลสินค้าหลัก
|
// ข้อมูลสินค้าหลัก
|
||||||
// export interface IProduct {
|
// export interface IProduct {
|
||||||
// id: string;
|
// id: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user