사이트·관리자 봉투 물류 기능(수불·통계·레포트·재고·발주)과 DB·메뉴·E2E를 운영 반영한다.

통계 분석(전년대비·월별·계절별), 수급계획·LOT 수불, 지정판매소·실사·메뉴 링크 등을 포함한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
taekyoungc
2026-06-01 16:15:15 +09:00
parent 21e7b91871
commit 0f1d414f37
129 changed files with 18068 additions and 1585 deletions

View File

@@ -62,7 +62,7 @@ test.describe('사이트 메뉴 (/bag/*) 페이지 접근', () => {
test('재고 관리', async ({ page }) => {
await page.goto('/bag/inventory');
await expect(page).toHaveURL(/\/bag\/inventory/);
await expect(page.locator('th:has-text("현재재고")')).toBeVisible();
await expect(page.locator('th:has-text("시군구 재고")')).toBeVisible();
});
test('판매 관리', async ({ page }) => {
@@ -77,16 +77,32 @@ test.describe('사이트 메뉴 (/bag/*) 페이지 접근', () => {
await expect(page.locator('th:has-text("봉투코드")')).toBeVisible();
});
test('봉투 수불 관리', async ({ page }) => {
await page.goto('/bag/flow');
test('기간별 봉투 수불 현황', async ({ page }) => {
await page.goto('/bag/flow?search=1&start_date=2026-01-01&end_date=2026-03-31&agg_mode=period');
await expect(page).toHaveURL(/\/bag\/flow/);
await expect(page.locator('th:has-text("현재재고")')).toBeVisible();
await expect(page.getByText('기간별 봉투 수불 현황')).toBeVisible();
await expect(page.locator('th:has-text("전일재고")')).toBeVisible();
await expect(page.locator('th:has-text("입고계")')).toBeVisible();
await expect(page.locator('th:has-text("출고계")')).toBeVisible();
await expect(page.getByRole('link', { name: '엑셀저장' })).toBeVisible();
});
test('통계 분석 관리', async ({ page }) => {
await page.goto('/bag/analytics');
await expect(page).toHaveURL(/\/bag\/analytics/);
await expect(page.locator('main >> text=Phase 6에서 구현 예정')).toBeVisible();
test('통계 분석 — 전년 대비 판매 분석', async ({ page }) => {
await page.goto('/bag/analytics/year-over-year?search=1&year=2025');
await expect(page).toHaveURL(/\/bag\/analytics\/year-over-year/);
await expect(page.getByText('전년 대비 판매 분석').first()).toBeVisible();
});
test('통계 분석 — 월별 판매 추이', async ({ page }) => {
await page.goto('/bag/analytics/monthly-trend?search=1&base_ym=2025-05');
await expect(page).toHaveURL(/\/bag\/analytics\/monthly-trend/);
await expect(page.getByText('월별 판매 추이 분석').first()).toBeVisible();
});
test('통계 분석 — 계절별 판매 추이', async ({ page }) => {
await page.goto('/bag/analytics/seasonal-trend?search=1&base_year=2025&season=spring');
await expect(page).toHaveURL(/\/bag\/analytics\/seasonal-trend/);
await expect(page.getByText('계절별 판매 추이 분석').first()).toBeVisible();
});
test('창', async ({ page }) => {

View File

@@ -37,15 +37,18 @@ const BAG_PATHS = [
'/bag/issue',
'/bag/issue/create',
'/bag/inventory',
'/bag/inventory/adjust',
'/bag/inventory/inspection-select',
'/bag/order/create',
'/bag/order/change',
'/bag/receiving/create',
'/bag/sales',
'/bag/sales-stats',
'/bag/sale/create',
'/bag/shop-order/create',
'/bag/flow',
'/bag/analytics',
'/bag/analytics/year-over-year',
'/bag/analytics/monthly-trend',
'/bag/analytics/seasonal-trend',
'/bag/window',
'/bag/help',
'/bag/waste-suibal-enterprise',

View File

@@ -150,7 +150,12 @@ test.describe('P5-04: 년 판매 현황', () => {
await loginAsLocal(page);
await page.goto('/bag/reports/yearly-sales');
await expect(page).toHaveURL(/yearly-sales/);
await expect(page.locator('table.data-table')).toBeVisible();
await expect(page.locator('#yearly-sales-table')).toBeVisible();
await expect(page.getByRole('button', { name: '조회' })).toBeVisible();
await expect(page.getByRole('link', { name: '엑셀저장' })).toBeVisible();
await expect(page.getByRole('button', { name: '인쇄' })).toBeVisible();
await expect(page.locator('select[name="gugun_code"]')).toBeVisible();
await expect(page.locator('select[name="sa_idx"]')).toBeVisible();
});
test('연도 변경 조회', async ({ page }) => {
@@ -168,7 +173,28 @@ test.describe('P5-05: 판매소별 판매현황', () => {
await loginAsLocal(page);
await page.goto('/bag/reports/shop-sales');
await expect(page).toHaveURL(/shop-sales/);
await expect(page.locator('table.data-table')).toBeVisible();
await expect(page.locator('#shop-sales-table')).toBeVisible();
await expect(page.getByRole('heading', { name: '지정 판매소별 판매현황' }).first()).toBeVisible();
await expect(page.getByRole('button', { name: '조회' })).toBeVisible();
await expect(page.getByRole('link', { name: '엑셀저장' })).toBeVisible();
await expect(page.getByRole('button', { name: '인쇄' })).toBeVisible();
await expect(page.getByRole('columnheader', { name: '지정판매소' })).toBeVisible();
await expect(page.getByRole('columnheader', { name: '1월' })).toBeVisible();
await expect(page.locator('input[name="metric"][value="qty"]')).toBeVisible();
});
test('월별 수량 셀에 음수가 표시되지 않음', async ({ page }) => {
await loginAsLocal(page);
await page.goto('/bag/reports/shop-sales?metric=qty');
await expect(page.locator('#shop-sales-table')).toBeVisible();
const texts = await page.locator('#shop-sales-table td.num-cell').allTextContents();
for (const raw of texts) {
const n = raw.replace(/,/g, '').trim();
if (n === '') {
continue;
}
expect(n.startsWith('-'), `음수 셀: ${raw}`).toBe(false);
}
});
});
@@ -176,14 +202,24 @@ test.describe('P5-05: 판매소별 판매현황', () => {
// P5-06: 홈택스 엑셀
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
test.describe('P5-06: 홈택스 세금계산서 엑셀', () => {
test('홈택스 엑셀 내보내기', async ({ page }) => {
test('홈택스 처리 화면 및 조회 표', async ({ page }) => {
await loginAsLocal(page);
// 다운로드를 트리거하는 URL이므로 evaluate로 fetch 테스트
const status = await page.evaluate(async () => {
const res = await fetch('/bag/reports/hometax-export');
return res.status;
});
expect(status).toBe(200);
await page.goto('/bag/reports/hometax-export');
await expect(page.getByRole('heading', { name: '홈택스 처리' }).first()).toBeVisible();
await expect(page.locator('#hometax-result-table')).toBeVisible();
await expect(page.locator('#hometax-result-table thead th').first()).toHaveText('전자세금계산서종류');
await expect(page.getByText('총 건수')).toBeVisible();
});
test('홈택스 엑셀 다운로드 응답', async ({ page }) => {
await loginAsLocal(page);
const y = new Date().getFullYear();
const res = await page.request.get(
`/bag/reports/hometax-export?search=1&start_date=${y}-01-01&end_date=${y}-12-31&write_date=${y}-05-14&export=1`
);
expect(res.status()).toBe(200);
const ct = (res.headers()['content-type'] || '').toLowerCase();
expect(ct).toContain('application/vnd.ms-excel');
});
});
@@ -195,13 +231,24 @@ test.describe('P5-08: 반품/파기 현황', () => {
await loginAsLocal(page);
await page.goto('/bag/reports/returns');
await expect(page).toHaveURL(/returns/);
await expect(page.locator('table.data-table')).toBeVisible();
await expect(page.getByText('반품/파기 현황')).toBeVisible();
await expect(page.locator('input[name="io_type"][value="in"]')).toBeVisible();
await expect(page.locator('input[name="io_type"][value="out"]')).toBeVisible();
await expect(page.locator('th:has-text("반품처")')).toBeVisible();
await expect(page.locator('th:has-text("종류")')).toBeVisible();
});
test('기간 필터 조회', async ({ page }) => {
test('기간·입출고 조회 및 엑셀', async ({ page }) => {
await loginAsLocal(page);
await page.goto('/bag/reports/returns?start_date=2026-01-01&end_date=2026-12-31');
await page.goto('/bag/reports/returns?search=1&start_date=2026-01-01&end_date=2026-12-31&io_type=out');
await expect(page.locator('table.data-table')).toBeVisible();
await expect(page.getByRole('link', { name: '엑셀저장' })).toBeVisible();
const res = await page.request.get(
'/bag/reports/returns/export?search=1&start_date=2026-01-01&end_date=2026-12-31&io_type=out'
);
expect(res.status()).toBe(200);
const ct = (res.headers()['content-type'] || '').toLowerCase();
expect(ct).toContain('application/vnd.ms-excel');
});
});
@@ -213,12 +260,15 @@ test.describe('P5-10: LOT 수불 조회', () => {
await loginAsLocal(page);
await page.goto('/bag/reports/lot-flow');
await expect(page).toHaveURL(/lot-flow/);
await expect(page.getByText('LOT 수불 현황')).toBeVisible();
await expect(page.getByLabel('봉투번호')).toBeVisible();
});
test('LOT 번호 검색', async ({ page }) => {
test('바코드 조회 폼', async ({ page }) => {
await loginAsLocal(page);
await page.goto('/bag/reports/lot-flow?lot=LOT-2025');
await expect(page).toHaveURL(/lot=LOT/);
await page.goto('/bag/reports/lot-flow?search=1&barcode=TEST-NOT-FOUND');
await expect(page).toHaveURL(/barcode=TEST-NOT-FOUND/);
await expect(page.getByRole('columnheader', { name: '입출고처' })).toBeVisible();
});
});
@@ -232,9 +282,17 @@ test.describe('P5-11: 기타 입출고', () => {
await expect(page).toHaveURL(/misc-flow/);
});
test('기타 입출고 등록 폼 표시', async ({ page }) => {
test('기타 입출고 화면 구성 (스크린샷·기능목록)', async ({ page }) => {
await loginAsLocal(page);
await page.goto('/bag/reports/misc-flow');
await expect(page.locator('select[name="flow_y"]')).toBeVisible();
await expect(page.locator('select[name="flow_m"]')).toBeVisible();
await expect(page.locator('select[name="flow_y"] option').first()).toHaveText('전체');
await expect(page.locator('select[name="flow_m"] option').nth(1)).toHaveText('1월');
await expect(page.locator('select[name="bag_kind"]')).toBeVisible();
await expect(page.getByText('입출고 리스트')).toBeVisible();
await expect(page.getByText('입출고 일자')).toBeVisible();
await expect(page.getByText('입출고 봉투 코드')).toBeVisible();
await expect(page.locator('select[name="bmf_type"]')).toBeVisible();
await expect(page.locator('input[name="bmf_qty"]')).toBeVisible();
});
@@ -278,12 +336,6 @@ test.describe('사이트 메뉴 CRUD 동작', () => {
await expect(page.locator('select[name="so_ds_idx"]')).toBeVisible();
});
test('재고 조정 폼', async ({ page }) => {
await page.goto('/bag/inventory/adjust');
await expect(page.locator('select[name="bag_code"]')).toBeVisible();
await expect(page.locator('select[name="adjust_type"]')).toBeVisible();
await expect(page.locator('input[name="qty"]')).toBeVisible();
});
});
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

View File

@@ -22,6 +22,14 @@ test.describe('P3: 발주 관리', () => {
await expect(page.locator('input[name="bo_order_date"]')).toBeVisible();
});
test('발주 변경 허브', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/bag/order/change');
await expect(page).toHaveURL(/\/bag\/order\/change/);
await expect(page.locator('select[name="month"]')).toBeVisible();
await expect(page.locator('legend:has-text("변경 구분")')).toBeVisible();
});
test('기간 필터 조회', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/bag/bag-orders?start_date=2026-01-01&end_date=2026-12-31');

View File

@@ -13,12 +13,18 @@ test.describe('P5: 판매 대장', () => {
test('일자별 판매 대장 접근', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/bag/reports/sales-ledger');
await expect(page).toHaveURL(/\/admin\/reports\/sales-ledger/);
await expect(page).toHaveURL(/\/bag\/reports\/sales-ledger/);
await expect(page.getByText('지정 판매소 판매 대장').first()).toBeVisible();
await expect(page.getByText('판매소명').first()).toBeVisible();
await expect(page.getByText('[지정판매소] 일자별 판매대장').first()).toBeVisible();
});
test('기간별 판매 대장', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/bag/reports/sales-ledger?mode=period&start_date=2026-01-01&end_date=2026-12-31');
await expect(page).toHaveURL(/mode=period/);
await expect(page.getByText('[지정판매소] 기간별 판매대장').first()).toBeVisible();
await expect(page.getByRole('columnheader', { name: '일자' })).toHaveCount(0);
await expect(page.getByRole('columnheader', { name: '지정번호' })).toBeVisible();
});
});
@@ -26,7 +32,14 @@ test.describe('P5: 일계표', () => {
test('일계표 접근', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/bag/reports/daily-summary');
await expect(page).toHaveURL(/\/admin\/reports\/daily-summary/);
await expect(page).toHaveURL(/\/bag\/reports\/daily-summary/);
await expect(page.getByRole('heading', { name: '일계표' }).first()).toBeVisible();
await expect(page.getByRole('button', { name: '조회' })).toBeVisible();
await expect(page.getByRole('link', { name: '엑셀저장' })).toBeVisible();
await expect(page.getByRole('button', { name: '인쇄' })).toBeVisible();
await expect(page.locator('#daily-summary-table')).toBeVisible();
await expect(page.getByRole('columnheader', { name: '구분' })).toBeVisible();
await expect(page.getByRole('columnheader', { name: '봉투종류' })).toBeVisible();
});
});
@@ -34,15 +47,25 @@ test.describe('P5: 기간별 판매현황', () => {
test('기간별 판매현황 접근', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/bag/reports/period-sales');
await expect(page).toHaveURL(/\/admin\/reports\/period-sales/);
await expect(page).toHaveURL(/\/bag\/reports\/period-sales/);
await expect(page.getByRole('heading', { name: /기간별 판매현황/ }).first()).toBeVisible();
await expect(page.getByRole('button', { name: '조회' })).toBeVisible();
await expect(page.getByRole('link', { name: '엑셀저장' })).toBeVisible();
await expect(page.getByRole('button', { name: '인쇄' })).toBeVisible();
await expect(page.locator('#period-sales-table')).toBeVisible();
await expect(page.getByRole('columnheader', { name: '품목' })).toBeVisible();
await expect(page.locator('select[name="mode"]')).toBeVisible();
});
});
test.describe('P5: 봉투 수불 현황', () => {
test('수불 현황 접근', async ({ page }) => {
test.describe('P5: 봉투 수급 계획', () => {
test('수급 계획 조회', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/bag/reports/supply-demand');
await expect(page).toHaveURL(/\/admin\/reports\/supply-demand/);
await page.goto('/bag/reports/supply-demand?search=1');
await expect(page).toHaveURL(/\/bag\/reports\/supply-demand/);
await expect(page.getByText('쓰레기봉투 수급 계획').first()).toBeVisible();
await expect(page.getByRole('columnheader', { name: '발주예정일' })).toBeVisible();
await expect(page.getByRole('columnheader', { name: '소진일수' })).toBeVisible();
});
});
@@ -50,8 +73,8 @@ test.describe('P5: 지자체관리자 접근', () => {
test('리포트 접근 가능', async ({ page }) => {
await login(page, 'local');
await page.goto('/bag/reports/sales-ledger');
await expect(page).toHaveURL(/\/admin\/reports\/sales-ledger/);
await expect(page).toHaveURL(/\/bag\/reports\/sales-ledger/);
await page.goto('/bag/reports/daily-summary');
await expect(page).toHaveURL(/\/admin\/reports\/daily-summary/);
await expect(page).toHaveURL(/\/bag\/reports\/daily-summary/);
});
});