Phase 5 판매대장/일계표/기간별현황/수불현황 리포트 구현

- SalesReport 컨트롤러 (판매대장, 일계표, 기간별판매, 수불현황)
- 판매대장: 일자별/기간별 모드 전환, 판매소별 품목 집계
- 일계표: 당일 판매 + 당월 누계 병렬 표시
- 기간별 판매현황: 품목별 판매/반품/합계 + 총합계
- 봉투 수불현황: 현재재고/입고/판매/불출 4섹션 그리드 레이아웃
- E2E 테스트 6개 전체 통과

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
javamon1174
2026-03-25 18:29:31 +09:00
parent 6e8bd84182
commit f451f0ff3b
7 changed files with 637 additions and 0 deletions

View File

@@ -0,0 +1,180 @@
<?php
namespace App\Controllers\Admin;
use App\Controllers\BaseController;
use App\Models\BagSaleModel;
use App\Models\BagIssueModel;
use App\Models\BagReceivingModel;
use App\Models\BagInventoryModel;
class SalesReport extends BaseController
{
/**
* P5-01: 판매 대장 (일자별/기간별)
*/
public function salesLedger()
{
helper('admin');
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
$startDate = $this->request->getGet('start_date') ?? date('Y-m-01');
$endDate = $this->request->getGet('end_date') ?? date('Y-m-d');
$mode = $this->request->getGet('mode') ?? 'daily'; // daily or period
$saleModel = model(BagSaleModel::class);
$db = \Config\Database::connect();
if ($mode === 'daily') {
$result = $db->query("
SELECT bs_sale_date, bs_ds_name, bs_bag_code, bs_bag_name, bs_type,
SUM(ABS(bs_qty)) as total_qty,
SUM(bs_amount) as total_amount
FROM bag_sale
WHERE bs_lg_idx = ? AND bs_sale_date BETWEEN ? AND ? AND bs_type IN('sale','return')
GROUP BY bs_sale_date, bs_ds_name, bs_bag_code, bs_bag_name, bs_type
ORDER BY bs_sale_date DESC, bs_ds_name, bs_bag_code
", [$lgIdx, $startDate, $endDate])->getResult();
} else {
$result = $db->query("
SELECT bs_ds_name, bs_bag_code, bs_bag_name,
SUM(CASE WHEN bs_type='sale' THEN ABS(bs_qty) ELSE 0 END) as sale_qty,
SUM(CASE WHEN bs_type='sale' THEN bs_amount ELSE 0 END) as sale_amount,
SUM(CASE WHEN bs_type='return' THEN ABS(bs_qty) ELSE 0 END) as return_qty,
SUM(CASE WHEN bs_type='return' THEN bs_amount ELSE 0 END) as return_amount
FROM bag_sale
WHERE bs_lg_idx = ? AND bs_sale_date BETWEEN ? AND ?
GROUP BY bs_ds_name, bs_bag_code, bs_bag_name
ORDER BY bs_ds_name, bs_bag_code
", [$lgIdx, $startDate, $endDate])->getResult();
}
return view('admin/layout', [
'title' => '판매 대장',
'content' => view('admin/sales_report/sales_ledger', compact('result', 'startDate', 'endDate', 'mode')),
]);
}
/**
* P5-02: 일계표
*/
public function dailySummary()
{
helper('admin');
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
$date = $this->request->getGet('date') ?? date('Y-m-d');
$db = \Config\Database::connect();
// 당일 판매
$daily = $db->query("
SELECT bs_bag_code, bs_bag_name,
SUM(CASE WHEN bs_type='sale' THEN ABS(bs_qty) ELSE 0 END) as sale_qty,
SUM(CASE WHEN bs_type='sale' THEN bs_amount ELSE 0 END) as sale_amount
FROM bag_sale
WHERE bs_lg_idx = ? AND bs_sale_date = ?
GROUP BY bs_bag_code, bs_bag_name
ORDER BY bs_bag_code
", [$lgIdx, $date])->getResult();
// 당월 누계
$monthStart = date('Y-m-01', strtotime($date));
$monthly = $db->query("
SELECT bs_bag_code, bs_bag_name,
SUM(CASE WHEN bs_type='sale' THEN ABS(bs_qty) ELSE 0 END) as sale_qty,
SUM(CASE WHEN bs_type='sale' THEN bs_amount ELSE 0 END) as sale_amount
FROM bag_sale
WHERE bs_lg_idx = ? AND bs_sale_date BETWEEN ? AND ?
GROUP BY bs_bag_code, bs_bag_name
ORDER BY bs_bag_code
", [$lgIdx, $monthStart, $date])->getResult();
return view('admin/layout', [
'title' => '일계표',
'content' => view('admin/sales_report/daily_summary', compact('daily', 'monthly', 'date')),
]);
}
/**
* P5-03: 기간별 판매현황
*/
public function periodSales()
{
helper('admin');
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
$startDate = $this->request->getGet('start_date') ?? date('Y-m-01');
$endDate = $this->request->getGet('end_date') ?? date('Y-m-d');
$db = \Config\Database::connect();
$result = $db->query("
SELECT bs_bag_code, bs_bag_name,
SUM(CASE WHEN bs_type='sale' THEN ABS(bs_qty) ELSE 0 END) as sale_qty,
SUM(CASE WHEN bs_type='sale' THEN bs_amount ELSE 0 END) as sale_amount,
SUM(CASE WHEN bs_type='return' THEN ABS(bs_qty) ELSE 0 END) as return_qty,
SUM(CASE WHEN bs_type='return' THEN bs_amount ELSE 0 END) as return_amount
FROM bag_sale
WHERE bs_lg_idx = ? AND bs_sale_date BETWEEN ? AND ?
GROUP BY bs_bag_code, bs_bag_name
ORDER BY bs_bag_code
", [$lgIdx, $startDate, $endDate])->getResult();
return view('admin/layout', [
'title' => '기간별 판매현황',
'content' => view('admin/sales_report/period_sales', compact('result', 'startDate', 'endDate')),
]);
}
/**
* P5-07: 봉투 수불 현황
*/
public function supplyDemand()
{
helper('admin');
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
$startDate = $this->request->getGet('start_date') ?? date('Y-m-01');
$endDate = $this->request->getGet('end_date') ?? date('Y-m-d');
$db = \Config\Database::connect();
// 입고 합계
$receiving = $db->query("
SELECT br_bag_code, br_bag_name,
SUM(br_qty_sheet) as recv_qty
FROM bag_receiving
WHERE br_lg_idx = ? AND br_receive_date BETWEEN ? AND ?
GROUP BY br_bag_code, br_bag_name
", [$lgIdx, $startDate, $endDate])->getResult();
// 판매 합계
$sales = $db->query("
SELECT bs_bag_code, bs_bag_name,
SUM(CASE WHEN bs_type='sale' THEN ABS(bs_qty) ELSE 0 END) as sale_qty,
SUM(CASE WHEN bs_type='return' THEN ABS(bs_qty) ELSE 0 END) as return_qty
FROM bag_sale
WHERE bs_lg_idx = ? AND bs_sale_date BETWEEN ? AND ?
GROUP BY bs_bag_code, bs_bag_name
", [$lgIdx, $startDate, $endDate])->getResult();
// 불출 합계
$issues = $db->query("
SELECT bi2_bag_code, bi2_bag_name,
SUM(bi2_qty) as issue_qty
FROM bag_issue
WHERE bi2_lg_idx = ? AND bi2_issue_date BETWEEN ? AND ? AND bi2_status = 'normal'
GROUP BY bi2_bag_code, bi2_bag_name
", [$lgIdx, $startDate, $endDate])->getResult();
// 현재 재고
$inventory = model(BagInventoryModel::class)->where('bi_lg_idx', $lgIdx)->findAll();
return view('admin/layout', [
'title' => '봉투 수불 현황',
'content' => view('admin/sales_report/supply_demand', compact('receiving', 'sales', 'issues', 'inventory', 'startDate', 'endDate')),
]);
}
}