From 8f54b43de9ab8c2f2604a399de4e58310a16239c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 17:50:58 +0900 Subject: [PATCH 01/19] =?UTF-8?q?docs:=20[sales]=20=EB=AC=B4=EB=A3=8C=20?= =?UTF-8?q?=EC=B2=B4=ED=97=98=20=EA=B4=80=EB=A0=A8=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=A0=84=EB=A9=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 가격정책 파트너 가이드: 4.3 무료 체험 섹션 삭제, FAQ Q6 삭제 - PPT 생성 스크립트: 무료 체험 카드/Q&A/전략/키포인트 제거 - 할인 종류 4가지 → 3가지로 변경 - 1년차 비용 계산에서 무료 체험 반영 제거 --- guides/generate-pricing-guide.cjs | 23 +++++++++--------- guides/price-simulator-partner-guide.md | 32 +++++++------------------ 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/guides/generate-pricing-guide.cjs b/guides/generate-pricing-guide.cjs index c29462a..ae6c961 100644 --- a/guides/generate-pricing-guide.cjs +++ b/guides/generate-pricing-guide.cjs @@ -442,14 +442,13 @@ async function main() { const s8 = pres.addSlide(); s8.background = { fill: C.white }; addFooter(s8); - addPageTitle(s8, '고객에게 제안할 수 있는 할인 4가지'); + addPageTitle(s8, '고객에게 제안할 수 있는 할인 3가지'); addSlideNumber(s8, 8, TOTAL); const discounts = [ { title: '개발비 할인', desc: '%, 정액, 전액면제', impact: '수당 영향 있음', impactColor: C.red, bg: C.redBg, border: 'FFCDD2', color: C.red, icon: '1' }, { title: '구독료 할인', desc: '최대 50%까지', impact: '수당 영향 없음', impactColor: C.green, bg: C.greenBg, border: C.greenBorder, color: C.green, icon: '2' }, - { title: '무료 체험', desc: '1주일(7일)', impact: '수당 영향 없음', impactColor: C.green, bg: C.blueBg, border: C.blueBorder, color: C.blue, icon: '3' }, - { title: '개발비-구독료 연동', desc: 'ON/OFF 선택', impact: '개발비 변동 시 영향', impactColor: 'F57F17', bg: C.orangeBg, border: C.orangeBorder, color: C.orange, icon: '4' }, + { title: '개발비-구독료 연동', desc: 'ON/OFF 선택', impact: '개발비 변동 시 영향', impactColor: 'F57F17', bg: C.orangeBg, border: C.orangeBorder, color: C.orange, icon: '3' }, ]; discounts.forEach((d, i) => { @@ -473,7 +472,7 @@ async function main() { s8.addShape(pres.ShapeType.roundRect, { x: 0.5, y: 4.3, w: 9, h: 0.6, rectRadius: 0.08, fill: { color: 'E8F5E9' }, line: { color: 'A5D6A7', width: 0.5 } }); s8.addText([ { text: '💡 영업 팁: ', options: { fontSize: 11, bold: true, color: C.green } }, - { text: '구독료 할인 + 무료 체험은 고객에게 혜택을 주면서 내 수당은 안 줄어드는 좋은 전략!', options: { fontSize: 11, color: '1B5E20' } }, + { text: '구독료 할인은 고객에게 혜택을 주면서 내 수당은 안 줄어드는 좋은 전략!', options: { fontSize: 11, color: '1B5E20' } }, ], { x: 0.7, y: 4.3, w: 8.6, h: 0.6, valign: 'middle', fontFace: 'Arial' }); // ═══════════════════════════════════════════════════════ @@ -499,7 +498,7 @@ async function main() { ], { x: 0.5, y: 1.2, w: 9, h: 0.7, align: 'center', valign: 'middle', fontFace: 'Arial' }); // 실전 예시 - s9.addText('실전 예시: 제조업 기본 패키지, 10% 할인, 무료 체험 1주일', { + s9.addText('실전 예시: 제조업 기본 패키지, 10% 할인', { x: 0.5, y: 2.1, w: 9, h: 0.35, fontSize: 11, bold: true, color: C.text, fontFace: 'Arial' }); @@ -508,9 +507,9 @@ async function main() { ['개발비 할인 10%', '2,000 × 10%', '-200만원', C.red], ['최종 개발비', '', '1,800만원', C.green], ['월 구독료', '', '50만원', C.blue], - ['유료 개월수', '12 - 7일(무료)', '약 11.77개월', 'CE93D8'], - ['연간 구독료', '50 × 11.77', '약 588만원', C.blue], - ['1년차 총 비용', '1,800 + 588', '2,388만원', C.orange], + ['유료 개월수', '12개월', '12개월', 'CE93D8'], + ['연간 구독료', '50 × 12', '600만원', C.blue], + ['1년차 총 비용', '1,800 + 600', '2,400만원', C.orange], ]; calcData.forEach((row, i) => { @@ -602,7 +601,7 @@ async function main() { // 4 Steps const steps = [ { num: '1', title: '상품 고르기', desc: '필수 상품 자동체크\n추가 상품 선택', color: C.green, bg: C.greenBg }, - { num: '2', title: '할인 설정', desc: '개발비/구독료 할인\n무료 체험 기간', color: C.blue, bg: C.blueBg }, + { num: '2', title: '할인 설정', desc: '개발비/구독료 할인\n연동 ON/OFF', color: C.blue, bg: C.blueBg }, { num: '3', title: '가입유형 선택', desc: '개인 가입\n단체 가입', color: C.orange, bg: C.orangeBg }, { num: '4', title: '결과 확인!', desc: '최종 비용\n내 수당', color: '7B1FA2', bg: 'F3E5F5' }, ]; @@ -654,7 +653,7 @@ async function main() { addSlideNumber(s12, 12, TOTAL); const tips = [ - { situation: '초기 비용 부담이 클 때', strategy: '개발비 할인 + 무료 체험 1주일', color: C.green, bg: C.greenBg }, + { situation: '초기 비용 부담이 클 때', strategy: '개발비 할인 + 구독료 할인 조합', color: C.green, bg: C.greenBg }, { situation: '매달 비용이 부담될 때', strategy: '구독료 할인 (최대 50%)', color: C.blue, bg: C.blueBg }, { situation: '기존 시스템이 있을 때', strategy: '이카운트 연동 모듈 추천', color: C.orange, bg: C.orangeBg }, { situation: '대형 제조업체', strategy: '기본 패키지 + 공정관리 + 품질관리', color: '7B1FA2', bg: 'F3E5F5' }, @@ -675,7 +674,7 @@ async function main() { const faqs = [ ['"왜 비싸요?"', '기존 ERP 5천만~1억 vs SAM 2천만원 + AI 포함'], ['"구독료가 아까운데?"', '자체 서버 월 200만+ vs SAM 월 50만원'], - ['"무료 체험 안 돼요?"', '1주일(7일) 무료 체험 가능!'], + ['"추가 할인 안 돼요?"', '구독료 최대 50%까지 할인 가능!'], ]; faqs.forEach((f, i) => { const fy = 4.5 + i * 0.3; @@ -710,7 +709,7 @@ async function main() { const keyPoints = [ '개발비 = 한 번 | 구독료 = 매달', '수당 = 최종 개발비 × 수당률', - '구독료 할인/무료 체험 → 수당 영향 없음', + '구독료 할인 → 수당 영향 없음', ]; keyPoints.forEach((kp, i) => { s13.addShape(pres.ShapeType.roundRect, { x: 2, y: 3.85 + i * 0.42, w: 6, h: 0.35, rectRadius: 0.06, fill: { color: '1B2838' }, line: { color: '2A3A4A', width: 0.5 } }); diff --git a/guides/price-simulator-partner-guide.md b/guides/price-simulator-partner-guide.md index fd4f42b..208a459 100644 --- a/guides/price-simulator-partner-guide.md +++ b/guides/price-simulator-partner-guide.md @@ -84,7 +84,7 @@ SAM의 요금은 **딱 두 가지**로 구성된다. ## 4. 할인/프로모션 (고객에게 제안할 수 있는 것들) -가격 시뮬레이터에서 **4가지 할인**을 조합할 수 있다. +가격 시뮬레이터에서 **3가지 할인**을 조합할 수 있다. ### 4.1 개발비 할인 @@ -102,18 +102,7 @@ SAM의 요금은 **딱 두 가지**로 구성된다. 예) 구독료 50만원/월 → 20% 할인 → 40만원/월 ``` -### 4.3 무료 체험 기간 - -서비스 시작 후 1주일(7일) 동안 구독료를 면제한다. - -| 무료 기간 | 1년 중 실제 내는 개월 | -|----------|:------------------:| -| 없음 | 12개월 | -| 1주일(7일) | 거의 12개월 | - -> **참고**: 1주일(7일) 무료 체험은 연간 비용에 미미한 영향을 주므로, 유료 개월수는 거의 12개월로 계산한다. - -### 4.4 개발비-구독료 연동 (선택) +### 4.3 개발비-구독료 연동 (선택) 개발비를 깎아주면 구독료도 같은 비율로 자동 조정하는 기능이다. @@ -141,7 +130,7 @@ SAM의 요금은 **딱 두 가지**로 구성된다. └─────────────────────────────────────────────────────┘ ``` -### 예시: 제조업 기본 패키지, 10% 할인, 무료 체험 1주일 +### 예시: 제조업 기본 패키지, 10% 할인 | 항목 | 계산 | 금액 | |------|------|-----:| @@ -149,9 +138,9 @@ SAM의 요금은 **딱 두 가지**로 구성된다. | 개발비 할인 | 2,000 × 10% | -200만원 | | **최종 개발비** | - | **1,800만원** | | 월 구독료 | - | 50만원 | -| 유료 개월수 | 거의 12개월 (1주일 무료) | 약 12개월 | -| 연 구독료 | 50 × 약 12 | 약 600만원 | -| **1년차 총 비용** | 1,800 + 약 600 | **약 2,400만원** | +| 유료 개월수 | 12개월 | 12개월 | +| 연 구독료 | 50 × 12 | 600만원 | +| **1년차 총 비용** | 1,800 + 600 | **2,400만원** | > 2년차부터는 구독료만 내면 된다: 50만원 × 12 = **600만원/년** @@ -297,7 +286,6 @@ SAM의 요금은 **딱 두 가지**로 구성된다. **Step 3: 할인 설정** - 개발비 할인: % 또는 정액 - 구독료 할인: 0~50% -- 무료 체험 기간: 없음 / 1주일(7일) - 개발비-구독료 연동: ON/OFF **Step 4: 가입 유형 선택** @@ -326,7 +314,7 @@ SAM의 요금은 **딱 두 가지**로 구성된다. ### Q2. 구독료를 깎아줘도 수당이 줄어드나? -**아니다.** 수당은 개발비에서만 계산한다. 구독료 할인, 무료 체험은 수당에 영향 없다. +**아니다.** 수당은 개발비에서만 계산한다. 구독료 할인은 수당에 영향 없다. ### Q3. 단체 가입이 수당률이 더 높은가? @@ -340,11 +328,7 @@ SAM의 요금은 **딱 두 가지**로 구성된다. **0원이다.** 개발비가 0원이면 수당도 0원이다. 전액 면제는 최저 개발비가 0원인 상품에서만 가능하다. -### Q6. 무료 체험 기간을 주면 나한테 손해인가? - -**아니다.** 무료 체험은 구독료에만 영향을 주고, 수당은 개발비 기준이므로 파트너 수당에는 영향이 없다. 오히려 고객 유치가 쉬워질 수 있다. - -### Q7. 2년차부터는 고객이 얼마를 내나? +### Q6. 2년차부터는 고객이 얼마를 내나? 2년차부터는 **구독료만** 낸다. 예를 들어 월 구독료 50만원이면 연 600만원이다. From 3ffb612917a80a00ad83171bb92597de5776d946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 17:52:34 +0900 Subject: [PATCH 02/19] =?UTF-8?q?chore:=20[sales]=20=EA=B0=80=EA=B2=A9?= =?UTF-8?q?=EC=A0=95=EC=B1=85=20PPTX=20=EB=AC=B4=EB=A3=8C=EC=B2=B4?= =?UTF-8?q?=ED=97=98=20=EC=A0=9C=EA=B1=B0=20=EB=B0=98=EC=98=81=20=EC=9E=AC?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- guides/sam-pricing-simple-guide.pptx | Bin 388505 -> 383293 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/guides/sam-pricing-simple-guide.pptx b/guides/sam-pricing-simple-guide.pptx index 712e0ed6ee5f9cb626d76d60306d9fa4696a7236..0c12fcc17e5a1b26f506d882fba62be95763af5d 100644 GIT binary patch delta 3058 zcmZ8j4Nw$U5}uhi4YDiUmfBv559}-GXF#(gDX!&m$4bc-2gI-Dntw=QC zR2;P&(k%^|NslpfcM$3S9c}ux zsWJf!*I;dHdY@+^YK<}({hu`w^1A#SEozjK$z7!b-{aH_6jqR!1hRXWNE za=7z$a|4fB*7D{8jxJ2;6Q$LD7^2UaE;o@&kDMX%4EoRUGEbrV&z2uIlVzKba@JA>dO+D*|qhQ2t&>aQAf+ z?y2a7Pup^!n#1v`=;~zl#VD-U#7<(4 zmtw*6RqO#Szora6)Wi$&Qe1$1>|5{zFPeh-JQ&# zeg2LgUb&!N!5fR!nON8*19pGGA`cCLtNPfzEJH@;KK7XCm*kuD&uhK((|<}jDd>My z&`W>qpwnr$)=JW?wVeLNqio$UOo(IQ6g(b_;}jpw{p1hmZk4V2(DUq@FxF9g6mNRV znZ#}xPN`>p=xkvFw9}PVJ++?2$(XcW4aV|yic=kHf+I0~p_+gVo0O%xL1aG2WASFO zoS}dAkR4HQd5JP!3pg8(k=u<8GWH|Xn@x<4<`67g081&2Rj+u=ey_;maYczTPRqFz zkABUnMbGPDaXv)(W*=2NahF9kpZFNhKQ4<-iD~WaltcYg-YMt%soPR%-L$Q$m0lV> zF9HtM*fPd({4TDWM=!k_s}hdo=`Mg%^OHvP(S@<>$fT&b@~nNv}jD#mD3J zzH)|ESg}m6E>pwf_)Ad!SDb?X+6|$4#ACHt<~CR6#9KHoTOFy#JW&gIvPKp9DSp(8 z3u`SWT(3)#OP9St4G!ukJ=M8)XXo)e$Ag1~o{!2Lo}%Ih2S3HeZ`lTPf2{i8)qgAV z>Dl<|kA_3=L_g2D14iqbaDB)E=+7`Yg;|0;rAIsSavT$)pYQlvp65g_1m`Y-5@Ou5 z2*e)vEd{X$cUSe6b)+?6Mrnv7mFkaI!=MmwH0ADhU_=&6#TyKKwVc{H6rydn5rUH} zYr63sCyD*nK&+ngM74sRo)52yJ@EchpDc9bsuB8s_Jf;Gm)ZkRIh2AFJHd-Ti?UM4 zr}3&M-hz#U!Aq4ua~FTCgb5_Wt00=>(khrp@`Kx8$Fo&1n3Retm`2jS8m5>aJFcmQ zVAE;GW7RO6ly_>3W%boS#|$mAW9aQ)c~;&Q9wRypRDp1t2qU!}ht>QlYD&%1wQ&T$ zSOZf@cGtj5BxCPD49OjLM7S^Sz*N3xbZB6YX`NIHiG&!iv{F~zff4V37FtP>g z?A@&lV?&f6T-5?L+J-G;-IvX@hXsT2Tnhy9>iGz!h{}>&EV&0EXpCYDu5SUWgC@W) z<5-dg@UNrZOZqO1xeuW{O5lmJ>jyJ!!F{lC-_Lh425}l}$0LR}?*o0ZV9$N9 zD@olVr7;R^A(ZXtnv;&XsgguLTW=yRSN1STTk)I?|J-W4)JA|Ktp=T@H&sFMW-HLQ z5q6NZ>n+tFY1g`j0EV>*_@p+&aWO;1{C0!m%ekrr?;4Rf$6rS3Z?jfr;+ZxW!FllE zS!7hoK5jL$hCfz)@q9a2Ie<^nE&>#{^8#>LySd9dlL9`&>1~E9yNj1FiPL>LUX2m^ zEZqa&tCE)A-{zm3C@cM?-tSWE+hNf7Bmo5LnH_@lQnET{1F^=#9YEiYc*-y;JCbo< zJA`82H^73)9VX$Y^V682&*sHfF|^YpT(S976LP{iZ<`4N-UnX{t2YVrsmclO*(7pL z>@*4UiAsc(CEG*=BR!%GV?1D2H{^h%7524j7c&5JJmL}UF+6P6F~Cbsh~Qa>Mw?;v zPA&o|J$)k%zbp^MQ4hd6oVsOD=4szBS(4}%{!F(tUjSTu3nFaEiQq+^6yyKKKtq;; Gq5U6JnoipQ delta 3437 zcmZWr2~<=^7VY}=tAQ2}lqCX^W*HS_6WM|rh8Z2jg&0L0HE5JXqft=MNk&0EPU4E- zcYH2H;b_h*aRG^bow%^bqO7feyLR+=j4Tn8Ic7$KPQCgc3<-z+uj{>A_r9un{pz1P z;lB5lyM01Dl}!fx^N7l^_m%iF^mq82VdhU|ndFwU!wJXv193c>kFD-F{@)Niu8bfE z;2}?CZHjFVM&(|2{>T#r-a!cxu zU!O~XXa>eGI9=u|z1SFUArfp*oHyPgh`woFyU&)!{tHi1`;L zZ0jYcRr_)xxYhOwGG;cyLwzL;KEhwp;V<7YBx~^DLnICE6(p7BA>3N4!`te+;3L=U zYS!Sb%{sj1x)l0HgugUXjosu&b@nwMzHwTE+YP4oh$EiS!iZt8E7@S!m8?8Tb%P5H zjHwah$v~n;2hp>3a(GL@I>`GM35Jdb(jhh5Q64U-at!@<08DA4X1Ke6`YXki3t^p6 zGC|5h+73?!2`p7|qD@ppXjB+S<&U7*Ft0`uxkSOd&!r^e>(B%>bQJxOz_@dA;O@W4 zy&-8OjfS;br1xwh-x?Yg9T^Umt+JOIJe?*IDDYslFfX0hLdp`l0k&=jU4r9{a(7U@ zn?zR-by+%{Mc}|mVrTR2qWLMw=3(G`T1tS}NyG#vZ&FvV?x7A)+)A^R%u{c|@;2%R zGZ^uJ23L6k_~uGU5bHqf;nYp)q8$Cy0Un=`{ULTHX@TtTX&8hamTFX=ztB|TWFBH} zJ@R$yp>6sh!Ok`GXDp=bwR$1tW_A@)%1=}NAqokOT9~Qmaee_&I>T&X?k3t_RMoMd z`wz)dgMlo?s!|VZqD$X&3G69g#`A}RKS?2w*FnZYL7Zf#SYJ*D>m#0Vz;WuLtel9sBUhgQ4)kMl9|oW;HapU zf?TlJkl?QJdc7yEYDOLHBdOIls2`OP3zhlxo_{4%?fYE5R@E1IzwJ>M^q?Xk0|KYt|G3QTpX+*cCYzISV{ ztNAMphWwRqd@6HSX_}lDB7S;~`l*^*P8YNkY=rf_Nve`ppQNrkECIxy_pLX$m|P9 zXrrEBb&w*UK83Hmp(IftRE{_j?$Coty%fh>sOK+RL_p8-ftO=hAtqipj%gcmVLa0| zBrAdSl^n3Uz549!$7Q|-Lz#MPD(mO#7}7%oD$`gQQfy1tDfX@A6ylI7|XN>XU7{) zLu@S5<_i6?GEJXLjzk0}#G{7=7ug83`4`#SXdhl=qtOm1*O-#Z*$A;Ic%j1~-Fv&7 z#Um=9g2kd;qT8bt8hcxX#u8Mijh9xkNr?NVlFdZxTE)f)*5mt!J=OzP84J<-r9$D)7C2C1u@@;Kk!MHrLg?Z6Jg(^??dBUG83}61> zWA2nP_Cd)N=8L`E&5XaI*WKJ;a22nrU5}hxcwk|ELIm3yiqLXf8OHcZ?%3blMCiqh z)P7=R)MAo_An$h0A~dwEtU{bCJ>V>Zxt{sJ_D*Vo_AC4+$uoL!g?Y(^ePx3(zmJ<^ zLOhXN3|+6wF#0NA!_c+5T>4dpFRGA*-qD#TdM-L;&UgJcFM!NOew;tf21}D(ih~0@ zW1$YcRZj_L7 From 21e7559c91ee86ab2e810972db16b1e5748dce2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 18:07:36 +0900 Subject: [PATCH 03/19] =?UTF-8?q?docs:=20[sales]=20=EC=9D=B4=EC=B9=B4?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=20=EC=97=B0=EB=8F=99=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=E2=80=94=20=EB=B0=94=EB=A1=9C=EB=B9=8C?= =?UTF-8?q?=EC=9D=80=20=EA=B8=B0=EB=B3=B8=20=ED=8F=AC=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 개별 모듈 가격표에서 '이카운트 연동' 행 제거 (별도 상품 아님) - 영업 전략 '이카운트 연동 추천' → '바로빌 부가 서비스 안내'로 변경 - 데모 테넌트 정책 '바로빌/이카운트' → '바로빌 등 외부 연동'으로 통일 - 브로셔/슬라이드 HTML 반영 - PPTX 재생성 --- brochure/v1/slides/brochure-2page-back.html | 4 ++-- features/sales/demo-tenant-policy.md | 5 ++--- features/sales/demo-tenant-usage-guide.md | 4 ++-- guides/generate-pricing-guide.cjs | 3 +-- guides/price-simulator-partner-guide.md | 2 +- guides/sam-pricing-simple-guide.md | 4 ++-- guides/sam-pricing-simple-guide.pptx | Bin 383293 -> 380935 bytes rules/customer-pricing.md | 2 +- rules/slides/customer-pricing/slide-03.html | 7 +------ 9 files changed, 12 insertions(+), 19 deletions(-) diff --git a/brochure/v1/slides/brochure-2page-back.html b/brochure/v1/slides/brochure-2page-back.html index a285d16..b0aca52 100644 --- a/brochure/v1/slides/brochure-2page-back.html +++ b/brochure/v1/slides/brochure-2page-back.html @@ -200,8 +200,8 @@

점검/납기 알림

-

이카운트 연동

-

기존 ERP 동기화

+

바로빌 회계

+

세금계산서·카드

diff --git a/features/sales/demo-tenant-policy.md b/features/sales/demo-tenant-policy.md index b769361..308e202 100644 --- a/features/sales/demo-tenant-policy.md +++ b/features/sales/demo-tenant-policy.md @@ -101,7 +101,6 @@ | 사용자 초대 | ✅ | 최대 5명 | | 리포트/통계 | ✅ | 제한 없음 | | 바로빌 연동 | ❌ | 실제 금융 연동 차단 | -| 이카운트 연동 | ❌ | 외부 시스템 연동 차단 | | 파일 저장 | ✅ | 최대 1GB | | AI 토큰 | ✅ | 월 10만 토큰 | @@ -184,7 +183,7 @@ class DemoLimitMiddleware // 2. 수량 제한 체크 $limits = $tenant->demo_limits; - // 3. 금지 기능 체크 (바로빌, 이카운트 등) + // 3. 금지 기능 체크 (바로빌 등 외부 연동) if ($this->isBlockedFeature($request)) { return response()->json(['error' => '데모에서 사용할 수 없는 기능입니다.'], 403); } @@ -226,7 +225,7 @@ class DemoDataSeeder 3. demo_expires_at = NULL 4. subscription → status='active', plan='starter'/'business' 5. 기존 데이터 유지 (마이그레이션 불필요) - 6. 추가 기능 활성화 (바로빌, 이카운트 등) + 6. 추가 기능 활성화 (바로빌 등 외부 연동) ``` --- diff --git a/features/sales/demo-tenant-usage-guide.md b/features/sales/demo-tenant-usage-guide.md index d5a1449..89537f4 100644 --- a/features/sales/demo-tenant-usage-guide.md +++ b/features/sales/demo-tenant-usage-guide.md @@ -141,7 +141,7 @@ POST /api/v1/demo-tenants/{id}/convert |------|---------|---------| | 테넌트 유형 | `DEMO_TRIAL` | `STD` (정식) | | 만료일 | 30일 제한 | 제한 없음 | -| 기능 제한 | 바로빌/이카운트 차단 | 전체 기능 사용 | +| 기능 제한 | 바로빌 등 외부 연동 차단 | 전체 기능 사용 | | 데이터 | 그대로 유지 | 그대로 유지 | > 고객이 체험 중 입력한 데이터가 정식 환경에 그대로 이어진다. 데이터 재입력이 불필요하다. @@ -290,7 +290,7 @@ php artisan demo:check-inactive --days=3 ``` 1. POST /{id}/convert 로 정식 전환 2. 고객 데이터는 그대로 유지됨 (재입력 불필요) -3. 바로빌, 이카운트 등 외부 연동 기능 활성화 +3. 바로빌 등 외부 연동 기능 활성화 ``` ### 7.4 체험 기간 부족 시 diff --git a/guides/generate-pricing-guide.cjs b/guides/generate-pricing-guide.cjs index ae6c961..ae99a03 100644 --- a/guides/generate-pricing-guide.cjs +++ b/guides/generate-pricing-guide.cjs @@ -172,7 +172,6 @@ async function main() { ['QR코드 관리', '1,020만원', '5만원'], ['사진/출하 관리', '1,920만원', '10만원'], ['검사/토큰 적용', '1,020만원', '5만원'], - ['이카운트 연동', '1,920만원', '10만원'], ]; // 테이블 헤더 @@ -655,7 +654,7 @@ async function main() { const tips = [ { situation: '초기 비용 부담이 클 때', strategy: '개발비 할인 + 구독료 할인 조합', color: C.green, bg: C.greenBg }, { situation: '매달 비용이 부담될 때', strategy: '구독료 할인 (최대 50%)', color: C.blue, bg: C.blueBg }, - { situation: '기존 시스템이 있을 때', strategy: '이카운트 연동 모듈 추천', color: C.orange, bg: C.orangeBg }, + { situation: '세금계산서·카드 관리 필요', strategy: '바로빌 부가 서비스 안내 (기본 포함)', color: C.orange, bg: C.orangeBg }, { situation: '대형 제조업체', strategy: '기본 패키지 + 공정관리 + 품질관리', color: '7B1FA2', bg: 'F3E5F5' }, { situation: '여러 업체를 묶을 수 있을 때', strategy: '단체 가입 (수당률 30%로 UP!)', color: C.red, bg: C.redBg }, ]; diff --git a/guides/price-simulator-partner-guide.md b/guides/price-simulator-partner-guide.md index 208a459..17df4b9 100644 --- a/guides/price-simulator-partner-guide.md +++ b/guides/price-simulator-partner-guide.md @@ -60,7 +60,7 @@ SAM의 요금은 **딱 두 가지**로 구성된다. | QR코드 관리 | 1,020만원 | 5만원 | 제품에 QR코드 부착, 이력 추적 | | 사진/출하 관리 | 1,920만원 | 10만원 | 출하 사진 촬영·등록, 배송 관리 | | 검사/토큰 적용 | 1,020만원 | 5만원 | 품질 검사, AI 토큰 활용 | -| 이카운트 연동 | 1,920만원 | 10만원 | 기존 이카운트 ERP와 데이터 연동 | + ### 3.3 통합 패키지 (대형 프로젝트용) diff --git a/guides/sam-pricing-simple-guide.md b/guides/sam-pricing-simple-guide.md index f6a46f5..a595101 100644 --- a/guides/sam-pricing-simple-guide.md +++ b/guides/sam-pricing-simple-guide.md @@ -58,7 +58,7 @@ | QR코드 관리 | 1,020만원 | 5만원 | 제품 이력 추적이 필요한 곳 | | 사진/출하 관리 | 1,920만원 | 10만원 | 출하·배송 기록이 중요한 곳 | | 검사/토큰 적용 | 1,020만원 | 5만원 | 품질검사 + AI 활용이 필요한 곳 | -| 이카운트 연동 | 1,920만원 | 10만원 | 기존에 이카운트를 쓰는 곳 | + ### 대형 패키지 (큰 프로젝트용) @@ -354,7 +354,7 @@ Step 1 Step 2 Step 3 Step 4 |----------|-----------| | 초기 비용 부담이 클 때 | 개발비 할인 + 구독료 할인 | | 매달 비용이 부담될 때 | 구독료 할인 (최대 50%) | -| 기존 시스템(이카운트)이 있을 때 | 이카운트 연동 모듈 추천 | +| 세금계산서·카드 관리가 필요할 때 | 바로빌 부가 서비스 안내 (기본 포함) | | 대형 제조업체 | 기본 패키지 + 공정관리 + 품질관리 | | 여러 업체를 묶을 수 있을 때 | 단체 가입 (수당률 33%로 UP!) | diff --git a/guides/sam-pricing-simple-guide.pptx b/guides/sam-pricing-simple-guide.pptx index 0c12fcc17e5a1b26f506d882fba62be95763af5d..bc10bc5cedf47239d1f84445cb9c7011fa67298e 100644 GIT binary patch delta 2811 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 diff --git a/rules/customer-pricing.md b/rules/customer-pricing.md index 7427563..5e0e155 100644 --- a/rules/customer-pricing.md +++ b/rules/customer-pricing.md @@ -36,7 +36,7 @@ SAM 서비스 도입 시 고객에게 안내하는 요금 체계를 정리한다 | QR코드 관리 | 1,020만원 | 5만원 | | 사진/출하 관리 | 1,920만원 | 10만원 | | 검사/토큰 적용 | 1,020만원 | 5만원 | -| 이카운트 연동 | 1,920만원 | 10만원 | + ### 2.3 통합 패키지 diff --git a/rules/slides/customer-pricing/slide-03.html b/rules/slides/customer-pricing/slide-03.html index 925e39a..0d49032 100644 --- a/rules/slides/customer-pricing/slide-03.html +++ b/rules/slides/customer-pricing/slide-03.html @@ -92,12 +92,7 @@

1,020만원

5만원

- -
-

이카운트 연동

-

1,920만원

-

10만원

-
+

통합 패키지

From 775cfb42626401bb66fb8f082c5c5f9275380ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 20:14:17 +0900 Subject: [PATCH 04/19] =?UTF-8?q?docs:=20[sales]=20=EB=8B=A8=EC=B2=B4=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EC=9C=A0=EC=B9=98=20=ED=8C=8C=ED=8A=B8?= =?UTF-8?q?=EB=84=88=203%=20=EC=98=81=EC=97=85=ED=8C=8C=ED=8A=B8=EB=84=88?= =?UTF-8?q?=20=EB=AC=B8=EC=84=9C=EC=97=90=EC=84=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 단체 가입은 '단체 수당 30%'만 표시 (유치 파트너 3%는 내부 정산용) - 수당 계산 예시 테이블 단체 컬럼 수정 - PPTX 슬라이드 반영 --- guides/generate-pricing-guide.cjs | 1 - guides/price-simulator-partner-guide.md | 11 ++++----- guides/sam-pricing-simple-guide.md | 30 ++++++++++++------------ guides/sam-pricing-simple-guide.pptx | Bin 380935 -> 378985 bytes 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/guides/generate-pricing-guide.cjs b/guides/generate-pricing-guide.cjs index ae99a03..31c5fac 100644 --- a/guides/generate-pricing-guide.cjs +++ b/guides/generate-pricing-guide.cjs @@ -290,7 +290,6 @@ async function main() { s5.addText('단체 가입', { x: 5.2, y: 2.85, w: 4.3, h: 0.45, fontSize: 14, bold: true, color: C.orange, align: 'center', valign: 'middle', fontFace: 'Arial' }); const groupItems = [ ['단체 수당', '30%', C.orange], - ['유치 파트너 수당', '3%', C.blue], ]; groupItems.forEach((item, i) => { const iy = 3.4 + i * 0.45; diff --git a/guides/price-simulator-partner-guide.md b/guides/price-simulator-partner-guide.md index 17df4b9..5b8eb26 100644 --- a/guides/price-simulator-partner-guide.md +++ b/guides/price-simulator-partner-guide.md @@ -166,12 +166,11 @@ SAM의 요금은 **딱 두 가지**로 구성된다. | | 개인 가입 | 단체 가입 | |---|:---:|:---:| | **파트너 수당** | **20%** | **30%** | -| **유치 파트너 수당** | 5% | 3% | -| **합계** | **25%** | **33%** | +| **유치 파트너 수당** | 5% | - | +| **매니저 수당** | 첫달 구독료 | - | -- **개인 가입**: 개별 고객이 직접 계약 -- **단체 가입**: 여러 고객을 묶어서 한꺼번에 계약 -- **매니저 수당**: 개발비 요율이 아닌, 첫달 구독료를 별도 지급 +- **개인 가입**: 개별 고객이 직접 계약 (파트너 20% + 유치 파트너 5% + 매니저 수당) +- **단체 가입**: 여러 고객을 묶어서 한꺼번에 계약 (단체 수당 30%) ### 6.3 수당 계산 예시 (제조업 기본 패키지) @@ -318,7 +317,7 @@ SAM의 요금은 **딱 두 가지**로 구성된다. ### Q3. 단체 가입이 수당률이 더 높은가? -**그렇다.** 단체 가입은 단체 수당 30% + 유치 파트너 3% = 33%이다. 개인 가입은 파트너 20% + 유치 파트너 5% = 25%이므로 단체가 더 높다. +**그렇다.** 단체 가입은 수당률 30%, 개인 가입은 파트너 20% + 유치 파트너 5% = 25%이므로 단체가 더 높다. ### Q4. 수당은 한 번에 받나? diff --git a/guides/sam-pricing-simple-guide.md b/guides/sam-pricing-simple-guide.md index a595101..3c9237a 100644 --- a/guides/sam-pricing-simple-guide.md +++ b/guides/sam-pricing-simple-guide.md @@ -103,14 +103,14 @@ ┌─ 개인 가입 ─────────────────┐ ┌─ 단체 가입 ─────────────────┐ │ │ │ │ │ 파트너 수당: 20% │ │ 단체 수당: 30% │ -│ 유치 파트너 수당: 5% │ │ 유치 파트너 수당: 3% │ -│ ───────────── │ │ ───────────── │ -│ 합계: 25% │ │ 합계: 33% │ +│ 유치 파트너 수당: 5% │ │ │ +│ ───────────── │ │ (여러 고객 묶어서 계약) │ +│ 합계: 25% │ │ │ │ │ │ │ -│ (개별 고객이 직접 계약) │ │ (여러 고객 묶어서 계약) │ +│ (개별 고객이 직접 계약) │ │ │ │ │ │ │ -│ + 매니저 수당: 첫달 구독료 │ │ + 매니저 수당: 첫달 구독료 │ -│ (별도 지급) │ │ (별도 지급) │ +│ + 매니저 수당: 첫달 구독료 │ │ │ +│ (별도 지급) │ │ │ └──────────────────────────────┘ └──────────────────────────────┘ ``` @@ -121,18 +121,18 @@ | | 개인 가입 | 단체 가입 | |---|---:|---:| | 파트너/단체 수당 | **400만원** | **600만원** | -| 유치 파트너 수당 | 100만원 | 60만원 | -| **합계** | **500만원** | **660만원** | -| + 매니저 수당 (별도) | 첫달 구독료 50만원 | 첫달 구독료 50만원 | +| 유치 파트너 수당 | 100만원 | - | +| **합계** | **500만원** | **600만원** | +| + 매니저 수당 (별도) | 첫달 구독료 50만원 | - | **10% 할인(1,800만원)으로 판매한 경우:** | | 개인 가입 | 단체 가입 | |---|---:|---:| | 파트너/단체 수당 | **360만원** | **540만원** | -| 유치 파트너 수당 | 90만원 | 54만원 | -| **합계** | **450만원** | **594만원** | -| + 매니저 수당 (별도) | 첫달 구독료 50만원 | 첫달 구독료 50만원 | +| 유치 파트너 수당 | 90만원 | - | +| **합계** | **450만원** | **540만원** | +| + 매니저 수당 (별도) | 첫달 구독료 50만원 | - | > 할인하면 내 수당도 줄어든다. **정가에 가깝게 팔수록 수당이 크다!** > 매니저 수당은 개발비 요율이 아니라, 첫달 구독료를 별도 지급하는 형태이다. @@ -356,7 +356,7 @@ Step 1 Step 2 Step 3 Step 4 | 매달 비용이 부담될 때 | 구독료 할인 (최대 50%) | | 세금계산서·카드 관리가 필요할 때 | 바로빌 부가 서비스 안내 (기본 포함) | | 대형 제조업체 | 기본 패키지 + 공정관리 + 품질관리 | -| 여러 업체를 묶을 수 있을 때 | 단체 가입 (수당률 33%로 UP!) | +| 여러 업체를 묶을 수 있을 때 | 단체 가입 (수당률 30%로 UP!) | ### 고객이 자주 하는 질문과 답변 @@ -394,11 +394,11 @@ Step 1 Step 2 Step 3 Step 4 ### Q3. 단체 가입이 뭔가? -여러 고객을 묶어서 한꺼번에 계약하는 것이다. 단체 수당 30% + 유치 파트너 3% = **합계 33%**를 받는다. +여러 고객을 묶어서 한꺼번에 계약하는 것이다. **단체 수당 30%**를 받는다. ### Q4. 유치 파트너 수당이 뭔가? -나를 유치한 상위 파트너에게 지급되는 수당이다. 개인 가입 시 개발비의 **5%**, 단체 가입 시 **3%**가 지급된다. +나를 유치한 상위 파트너에게 지급되는 수당이다. 개인 가입 시 개발비의 **5%**가 지급된다. ### Q5. 매니저 수당이 뭔가? diff --git a/guides/sam-pricing-simple-guide.pptx b/guides/sam-pricing-simple-guide.pptx index bc10bc5cedf47239d1f84445cb9c7011fa67298e..600f57bf83898762b81f64a02e32c0e65abe93a1 100644 GIT binary patch delta 2712 zcmZ`*4Nz276n^jCJz^VyC@u=CEsK983z$M`icnM5VE&AyVy0=9S^TVuKTRs*^kdc} z(lbvf`7>qZjDm&s78m4S)MfpdBCSjo%`wdo%W^8q?z#8rL^J8UciuVoJKuNCdH3CA z_12gR>tezuW^tQcqksPF;&3+;_muv0pXw5KBab)x3PyAe5;CZFr=&K0D3NV*7>O)Ov3SVju>s0+7&{s138cgLmi3{> zKUgA}LHr(J%4hIWik64Rzrx7AozT*yt3TU2W5)x7PM7E3{Td4!GGyeCp(9g`>xtuU zJx}ta68d$|B|F(A!~dd&%NJ+Qk02YGH%=GUNSc3++@8-r$;lSvUXmhnyoIz}es;m- z0aC1N)6n1W+hMx9{QA3RO)slIb@69m+38Kz6D*_NhDGP88FF4@f5G5VE-Qu)MF-TF#p^VDa$ls0SjSn#*} z0vy@iuCZQ*3&@&bohSEa*4_;Egu#-tzhJMhHMdK-4zZMzN2=J3aHAh0{+ zs+o3G-@&kTv{(r|!>5?-kt{;TKKS+Zko&G zSf<8N883DXMUE!0x~($%-d=N*@6`ydjeGpI1av$7x3*O>WaDf<2!ITPk;8sR)g~|{?CmyQ+I+ci+ zJ}t#LexE`J>9kwHMk^@OY<`vI@xv%Wyx@oX$(H$H4A~C9qPwdSM#*W>NrJhRD*90+ zWD)9@O2{CaTBYpLDpl_1Rf^@B<)`h3M+x`pewaqKbw6ZERyr~^G=KxstJSc))rz^Q z8m17kXN`qieUGP9A`5CDMdp58V>0Y$63KNnkSs&;7&+c3LXovFlJc`^A(QOdS{O(6 zR4rsl=!o#_uTerDtW(fcb(R#JCQ>R?XGtwtRB}T}CDlVR@yx1+DgQZCu)bcs_&@5w z$wx#n%&dkO`hXo$@q6FY;gWVym@PB!psAZAiKJ8DB>X+77y#j zRl_9*tcF+J^=Xc3xJP$R4P*NOC95}qlZATWolP*D%E%Tz|0R}Vshc}+ZIi0Fgre`| zShV8YvEwOg=^`y{&r_`Z4nmZy#d~eSCvtRb;bP);qB~E{ zJ0t4j^nTO`d-E)I9E2pQ%XLU(;5j8T{g5g?R|MJIOG+T)st}-&xoKo2^FDEno3Ckf zIc`o%+NJGWGiG;p;Gc(7bGIE9EZ3&u>;Uw}jKe}#=uHzr4%2O7SWcs(4@k6Zt>Lkf zMsx+moWFd^rgp{d=WMr2Y0A7obQk7ep8)ie2{n80K8z$@^D7cjAINu$(!Ri%6m2|- zy927rP(Y}zZ?$M$%|L%_;D}~$+IF=h!)kEa2>>Rz~~S=HT4(bnNW zbz@ji%_%l0f~8|XRXaThF>+gNIzKjJuUpgT>O7mxC delta 2736 zcmZ8j3s6+&6~6bs_YksDT!=0RiwdYTk;ldd;1fw^)J75^1~t`Gh+0D`umm6JjE~q! zHJwb1+dJ_%YDOGmn=uLt3*2RSEg)E1Dao`}ZHxhlLsK_VCT%hrBmK|+UyT-K@44rk z?|kQR|9f`7+!wQ_DkdU-iKg2W`U@@gL?khObxo%0ii!Mclre|rT|4PVQ%KL@dVdV* zx29TtW3Ea7?us|IyTc!1GzL^MMm}sLWUm@cK?CY^B0K6ZQdyK_Nh49lrbx|UoMe=b zBOS)4EP-xsvs4m4)uwUbTdh@-)fbWYCL{U#}kfG)aUNs7o^Q3^jm2m8_6x%KTyNI%`cWjlI-7*r*dhems}%t?5i(nBsZ0YM3R@? zJIU=Ethg~nB&#_z{abv0P)qXNIdE0D`Vamkj3B3uMv0K%F^J88<2%W@oDk3vqWy9fRrD)vs{d4Bdu%pHvGm1=vqa?x^l(IsTGoEEw{+?<>Ge=);=7*EtovW`we!I-H+gKUHJQqBQ%bN5R=9LKYPrab6 zL4Nx@`ERMX-_A~nes99eBeLJpC?>~K@EDWh86U?_5g)Soq?2`43HZg6Q_o0vLaPN= z+b#HCyDj*@8Rl9n;3H=v7YaDuZ37$rd!Cxr_=UDp#r$@F{m~j7HFH9=Ci8`2HJNWD zPWzxh5M*}7AD8)hH)^uH+A<5iW3vVKY}NFQ#Ds&IjfuGLFI9{)u3VmC_FdDC(7`d? zA&wRg@9>5XC+3wXN?r-7YxNlO4_|AmN<`GqCv^dATL;Nz>Pmf(&YcGfCuYwj)aH)VZlGSXu+#Kx8Mx}`jkRq`YaP71i|%ekd%QOkd%G7FqA|X z|2mEl-2ST{@4?}0NHM?pB~-~*@R47KZ^2*=q?vyzg?>@qiE{Y+c#3>uFN6v9xAuWY zj5WR*HW3CrH9)U6cGm#CqW^Nix@jT)`?9P|yz!tTMqROz8&?deGa#O<$XkLFfs7SWa@QA96_s{E#Q0mx3mf z&`C{@Axy8woHx-E;L#OLFq_b)nq<_?Cdeawen8g#c0jTY2CQ1dN)oj^)oj(WrSkBI zOt7z6*3!`o%l~gR;p7(iz*o0`lU;~KZ!5$if6Z-pxP_lHp7R9vXoGRg7(N(}U$j7! zsP%4vvR>4xc@r7#3TcU$ANN4i(pJvbm5imWkSqekR-g*mp3{^iow)lsN;7&ScU&9L z7ZRqmabr=hWZmDxNpJfhVj}HSbKUROjx16X`ivFdq4zG8eR!JzjI*&2~AH zyJXGUWRdf=W5fAD>Bp(DD*}u$f_IQDgL8ulNVV00%ql4xi@?lMJj+?$t> zW53lDqd3}uFLlb|f7Qv0e{YR0hc zMt1RIGi?ZqPQxtf^U!H<>WjYA6~i;e5t+Y4Wn+|kKdJC@?$pZXgY02!H_(q6Oz-B_ z?F)d)$C7S&(zcN?ZxK-GN!Z#Ajzk*Rp`k?!8j=)+J`WG9_-BCqx%@nh>VXJRdf)3O zucBz{nT=M;{&g_w#|I^rwCHVRnam#6WA!F>HT9;YY_@9nu7`KZ%N>1&cQ|(|C`Q$2 zr_o*kn14n-qF->27QG+Pvlr4t8p8XzH9YPBC~o(r3}ZrO0^Z_y(apHZ>QPUqib9{% ehaUf_2Ef#6NOR;bfiUu{;Qt@TDoUaWT>Bra1P5gR From 380bed83ed46fb61b3886ef498e0d4b685c80258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 20:33:07 +0900 Subject: [PATCH 05/19] =?UTF-8?q?chore:=20[sales]=20customer-pricing=20PPT?= =?UTF-8?q?X=20=EC=9E=AC=EC=83=9D=EC=84=B1=20(=EC=9D=B4=EC=B9=B4=EC=9A=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=9C=EA=B1=B0=20=EB=B0=98=EC=98=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HTML→PPTX 변환 스크립트 추가 - 이카운트 연동 행 제거된 슬라이드 반영 --- rules/customer-pricing.pptx | Bin 294356 -> 291312 bytes rules/slides/customer-pricing/convert.cjs | 27 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 rules/slides/customer-pricing/convert.cjs diff --git a/rules/customer-pricing.pptx b/rules/customer-pricing.pptx index f96693b7870866e564e9400ce95fa43d0687fe5c..4d5d2971329ef883f54e3f4d76e0d5b706aaffbb 100644 GIT binary patch delta 8095 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 S0P82Sy~VVLXJ;OT{r?NkQFYM( diff --git a/rules/slides/customer-pricing/convert.cjs b/rules/slides/customer-pricing/convert.cjs new file mode 100644 index 0000000..feaf880 --- /dev/null +++ b/rules/slides/customer-pricing/convert.cjs @@ -0,0 +1,27 @@ +const path = require('path'); +const fs = require('fs'); +module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); + +const PptxGenJS = require('pptxgenjs'); +const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); + +async function main() { + const pres = new PptxGenJS(); + pres.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 }); + pres.layout = 'CUSTOM_16x9'; + + const dir = __dirname; + const slides = fs.readdirSync(dir) + .filter(f => f.startsWith('slide-') && f.endsWith('.html')) + .sort(); + + for (const file of slides) { + await html2pptx(path.join(dir, file), pres); + } + + const output = path.join(dir, '..', '..', 'customer-pricing.pptx'); + await pres.writeFile({ fileName: output }); + console.log('PPTX created:', output); +} + +main().catch(console.error); From 253b9defd5aac62f4b06f305de08c348d0f9ac07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 20:35:20 +0900 Subject: [PATCH 06/19] =?UTF-8?q?fix:=20[sales]=20=EB=8B=A8=EC=B2=B4=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=ED=95=A9=EA=B3=84=2033%=20=E2=86=92=2030%?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- guides/generate-pricing-guide.cjs | 2 +- guides/sam-pricing-simple-guide.pptx | Bin 378985 -> 378985 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/generate-pricing-guide.cjs b/guides/generate-pricing-guide.cjs index 31c5fac..f0f081f 100644 --- a/guides/generate-pricing-guide.cjs +++ b/guides/generate-pricing-guide.cjs @@ -305,7 +305,7 @@ async function main() { s5.addShape(pres.ShapeType.roundRect, { x: 1.5, y: 4.75, w: 2, h: 0.35, rectRadius: 0.06, fill: { color: C.green } }); s5.addText('합계 25%', { x: 1.5, y: 4.75, w: 2, h: 0.35, fontSize: 11, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); s5.addShape(pres.ShapeType.roundRect, { x: 6.2, y: 4.75, w: 2, h: 0.35, rectRadius: 0.06, fill: { color: C.orange } }); - s5.addText('합계 33%', { x: 6.2, y: 4.75, w: 2, h: 0.35, fontSize: 11, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + s5.addText('합계 30%', { x: 6.2, y: 4.75, w: 2, h: 0.35, fontSize: 11, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); // ═══════════════════════════════════════════════════════ // 슬라이드 6: 수당 계산 예시 diff --git a/guides/sam-pricing-simple-guide.pptx b/guides/sam-pricing-simple-guide.pptx index 600f57bf83898762b81f64a02e32c0e65abe93a1..686efbb9f6b3696206c03f5eba6d58821c530bf3 100644 GIT binary patch delta 1986 zcmY+EeN2^g6vug<=N@H@5y%1p#COQ#U3r;`h;6MUjTeV#k~5MRro1ZJW>HO-&6(Wp z*3YR0tW0Of+XbG>)O5bc~Iit*!RBlFzza*A{E&KJ&Fi=O*Xh1GFuW1=lH;py^M zX2cpKk3~#)U@T9Z!I!lj*k0g;aV$SoF`^~1;4Y?5?|D_jxPRXS8RQmM;I`FOG8IZj z^=DT9DL!>+ORbMi-KMRv)8EWPTLHY zlG10^D5Ry2$14Qubx8`*dQY-4T}t@RySzgfzuqKqN3+CFv>Uh=9>ks#gJq>#+}ujH zSf)z+bZ4hBlBq_o5Yu@|xOgzBtP~qvS{4aDfVe6a+~=dRe}=Eh{vmSb6@Z4 zV9DVWxVID;+irs)ZQJedJ`mH15T-wT1FHG^p8uA6xo{*ygnoG+j8S_Ri{Qr)tbF$) zm`CczD#1+08dL}SkrC#4z_}6q^$>^gQ9VRsjBJ2-j60ejSiIi=L6}rEKoZ8A4X{o^ z!6Km%0_A3~C~AZyn7m{)%IX>c|4m?7uz2|RPHYqJ80t6)Of)octzjK3rkWm84YU5Q z`aI$%t*{>BkQFvz^l64t zsf1p0=qjVj+aQXT4*8Gh!fZFHOWI&9(wEw}>OmW%V!px7y&t!;s>?2Wp_!*%Hnq!M z%FE8qu|Z`!_tMi2TgeD#Uo7h2+so--?Ta0-nRaJ0-14{$-%f5Lty9|2Xp>D{Cx0Mg zo!n7G7k6~DOQJN^gx=|r9lf`*aW8ii+bv)DaDIS|y(+rdtG%0FdA6I|N_EJ#ivLQx zz-Wtu(P@YD@;Sd@xgf9f9_fX{&0e4N@Y@gfaED<|{#xF2^1a%fY~+@)v27`TWa}`yW~7-0T1V delta 1986 zcmY+EeN2^g6vug<=N@H@5#Ry?d{@u}~j zZh7F{@<8v5RL$h&fq#x{v-e`vct$P5=eC8$)3R3$Fqipael862BqJXU#Qg7-a^I4u zAYg0-Tiz)4d`!_Yrr@H_c=V(81|~6oCR2_RrE8+0L!oH zJpB2O8jfMP7Gnf!j#h)KR_{f}Rg51j8Jy+m$S%(La?|n=Yths1st_H!HZIQcFg!#4 z%8Xcz~u3!8n?irWnx@nSUG8CtrP0!??e2oD6b{kK(q~U2-p! zjB3xs{wY3oYm2Q9PT!=h(TZH5UqD6DO06{|Gdk0KI7LKB<^gK_B?}h%sqDXbj>`V?gN!RO-a?NK zQC(Gp4^^-1V|-ql#FY+-f7>JRu|75GS;A+&_IZYIG2ISc;B;#D^!-ZW+k%|fN?@!(AYzpnw7 zv?l>e4kW{!#mLxp8w_dNZhrTUm|6p2`u$g+iofsKuX`R8&LoJ?FBifnwRf=yew>e$ zZ@mw*Nd06vnCV!9YhXVz!dweDH=?H&;xIm_g(!@Xbr6qnM+1b2x9cDnlgc_+k8z|9 z)=4NtB-BHY+zb&#^{@z&=d4CqO+Dbh2`mc{_aEJfY2Y109VdZ_x(2Q_tV6`ThDTJx ztpBTSLcGihi5UB>kb=>#5t1-wHnQBQMo6TC!l4%fa@Dg5QjyAPf{hr3jKfX*R&$$K zCA*pP)6MWaa(9|xJH})kl1Y^&+-ZXVQKdr^Ek#yTm_=Qyu{5E>Dx??KU@OK$Hb};3 zw?P`A!%kggbZHB$qNT%ugSs%=jp~vXSc~+f7Ot9Xfi%oF*tz#pc2;%UWiK@I)Juv( z_HuCF*%>w{cW^JA4%kXYIQwEjE8kvDD{EhDg;d&|-t4i5+nCeFZKStJ8yao0scGX6 zWVDStifHGKPP9vu#+uMu?Xsg|E9wilqnHl)%183%+Su!82YWd>_?4$SxUDp&Y^(UM z^b3qOIT@XDN-w`JHY^q7mC-4^aJbp)lTLp7fllr)%*9{JYc9T5yNiuHyI>oh@k?Eh zLgzrETjDaVqq7U5Y3cgD_#cE5mwdaW(TQ~5uY^yRv9zIMTxg vgFVt<;BfU#K?ZTX(tt*q4376o1Da?u_`a77p6FxYoqha8+GIZaGUWaTccc@h From 2073af1d8e2e400723fb1e76f2f7876d71b30cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 21:08:48 +0900 Subject: [PATCH 07/19] =?UTF-8?q?docs:=20[rd]=20=EB=B0=A9=ED=99=94?= =?UTF-8?q?=EC=85=94=ED=84=B0=20=EA=B0=80=EC=9D=B4=EB=93=9C=EB=A0=88?= =?UTF-8?q?=EC=9D=BC=20SVG/3D=20=EB=A0=8C=EB=8D=94=EB=A7=81=20=EA=B8=B0?= =?UTF-8?q?=EC=88=A0=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 철재스라트 15세그먼트 절곡 프로파일 상세 - 스크린형 4부재 구성 및 치수 라벨 체계 - 2D SVG 좌표계 및 3D Three.js 변환 매핑 - INDEX.md, README.md 등록 --- INDEX.md | 2 + features/rd/README.md | 6 +- .../rd/fire-shutter-drawing-guide-rail.md | 203 ++++++++++++++++++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 features/rd/fire-shutter-drawing-guide-rail.md diff --git a/INDEX.md b/INDEX.md index ef94ff0..46bab30 100644 --- a/INDEX.md +++ b/INDEX.md @@ -172,6 +172,8 @@ DB 도메인별: | [approvals/mng-api-comparison.md](features/approvals/mng-api-comparison.md) | 결재관리 MNG↔API 비교 분석 및 React 구현 가이드 | | [email/README.md](features/email/README.md) | 이메일 시스템 (테넌트별 SMTP 설정, 프리셋, 연결 테스트) | | [construction-pmis/bim-viewer.md](features/construction-pmis/bim-viewer.md) | BIM 뷰어 (Three.js 기반 웹 3D 건물 모델 뷰어) | +| [rd/README.md](features/rd/README.md) | R&D 메뉴 전체 개요 | +| [rd/fire-shutter-drawing-guide-rail.md](features/rd/fire-shutter-drawing-guide-rail.md) | 방화셔터 가이드레일 SVG/3D 렌더링 기술 명세 | --- diff --git a/features/rd/README.md b/features/rd/README.md index 1483160..a095c96 100644 --- a/features/rd/README.md +++ b/features/rd/README.md @@ -20,6 +20,7 @@ R&D 메뉴는 SAM 플랫폼의 **연구개발 및 내부 도구** 모음이다. | **README.md** (이 문서) | 전체 개요, 메뉴 구조, 컨트롤러 매핑 | | [planning-design.md](planning-design.md) | 기획디자인 스토리보드 에디터 기술 명세 | | [design-insight.md](design-insight.md) | 디자인 인사이트 UI/UX 연구 도구 (100종 패턴, AI 프롬프트) | +| [fire-shutter-drawing-guide-rail.md](fire-shutter-drawing-guide-rail.md) | 방화셔터 도면 가이드레일 SVG/3D 렌더링 | ### 1.3 하위 메뉴 구조 @@ -35,7 +36,8 @@ R&D │ ├── 편집 /rd/ai-quotation/{id}/edit │ └── 문서 /rd/ai-quotation/{id}/document ├── 기획디자인 /rd/planning-design -└── 디자인 인사이트 /rd/design-insight +├── 디자인 인사이트 /rd/design-insight +└── 방화셔터 도면 /rd/fire-shutter-drawing ``` --- @@ -96,6 +98,7 @@ if ($request->header('HX-Request')) { | AI 견적 | Blade + Alpine.js | AiQuotationService | ai_quotations | 운영 중 | | **기획디자인** | **Blade + Alpine.js (SPA)** | **없음 (localStorage)** | **없음** | **운영 중** | | **디자인 인사이트** | **Blade + Alpine.js (SPA)** | **없음 (localStorage)** | **없음** | **운영 중** | +| **방화셔터 도면** | **Blade + JS (SVG/Three.js)** | **RdController** | **없음** | **운영 중** | --- @@ -103,6 +106,7 @@ if ($request->header('HX-Request')) { - [기획디자인 스토리보드 에디터](planning-design.md) — 블록 에디터, 서식, 인쇄, 내보내기 - [디자인 인사이트](design-insight.md) — UI/UX 연구 도구 (100종 패턴, AI 프롬프트 복사) +- [방화셔터 도면 가이드레일](fire-shutter-drawing-guide-rail.md) — 가이드레일 SVG/3D 렌더링 기술 명세 - [조직도 관리 기술문서](../../projects/org-chart/) — 조직도 시스템 상세 (별도 프로젝트 문서) --- diff --git a/features/rd/fire-shutter-drawing-guide-rail.md b/features/rd/fire-shutter-drawing-guide-rail.md new file mode 100644 index 0000000..a279ade --- /dev/null +++ b/features/rd/fire-shutter-drawing-guide-rail.md @@ -0,0 +1,203 @@ +# 방화셔터 도면 — 가이드레일 렌더링 + +> **작성일**: 2026-03-16 +> **상태**: 운영 중 +> **파일**: `mng/resources/views/rd/fire-shutter-drawing/index.blade.php` +> **라우트**: `GET /rd/fire-shutter-drawing` + +--- + +## 1. 개요 + +### 1.1 목적 + +방화셔터 가이드레일의 **2D 평면도(SVG)**와 **3D 렌더링(Three.js)**을 구현한다. +제품 유형(스크린형/철재스라트)에 따라 다른 프로파일을 렌더링하며, 각 부재(마감재, 본체, C형, 벽연형)의 절곡 치수를 시각화한다. + +### 1.2 제품 유형별 차이 + +| 항목 | 스크린형 | 철재스라트 | +|------|---------|-----------| +| 본체 형상 | C채널 (lip-flange-sideWall-backWall) | 15세그먼트 절곡 프로파일 | +| 마감재 절곡 | 10-11-110-30-15-15-15 (J-hook) | 10-13-120-25-15 (상단) / +19-14-15 (하단) | +| 벽연결 부재 | ③ C형 30-45-30 + ④ D형 11-23-40-23-11 | ④ 벽연형 30-45-30 | +| 전체 치수 | depth × width (가변) | 130mm × 75mm | + +--- + +## 2. 철재스라트 가이드레일 + +### 2.1 부재 구성 + +``` +┌──────────────────────────────────────────────────┐ +│ ① 마감재 SUS 1.2T (빨간색, 상/하 2장) │ +│ ┌────────────────────────────────────────────┐ │ +│ │ ② 본체 EGI 1.55T (회색, 15세그먼트 절곡) │ │ +│ │ ┌──────────────────────────────────────┐ │ │ +│ │ │ 개구부 (슬랫 통과 공간) │ │ │ +│ │ └──────────────────────────────────────┘ │ │ +│ └────────────────────────────────────────────┘ │ +│ │ +│ ④ 벽연형 EGI 1.55T (갈색, ㄷ자 30-45-30) │ +│ ■■■■■ ← 방화벽 │ +└──────────────────────────────────────────────────┘ +``` + +### 2.2 ② 본체 절곡 프로파일 (15세그먼트) + +정석 도면 기준, 시작점 `(0, 60)`: + +| Seg | 방향 | 치수(mm) | 좌표 | 설명 | +|-----|------|---------|------|------| +| 1 | 10→ | 10 | (0,60)→(10,60) | 좌측 립 | +| 2 | 60↑ | 60 | (0,60)→(0,0) | 좌측벽 상부 | +| 3 | 90→ | 90 | (0,0)→(90,0) | 상단 플랜지 | +| 4 | 21↓ | 21 | (90,0)→(90,21) | 상부 립 | +| 5 | 78← | 78 | (90,21)→(12,21) | **상부 내부 선반 (넓은)** | +| 6 | 30↓ | 30 | (12,21)→(12,51) | 내부 벽 | +| 7 | 43→ | 43 | (12,51)→(55,51) | **하부 내부 선반** | +| 8 | 15↓ | 15 | (55,51)→(55,66) | 스텝 하강 | +| 9 | 20→ | 20 | (55,66)→(75,66) | 스텝 수평 | +| 10 | 15↑ | 15 | (75,66)→(75,51) | 스텝 상승 | +| 11 | 15→ | 15 | (75,51)→(90,51) | 스텝 수평 | +| 12 | 21↓ | 21 | (90,51)→(90,72) | 하부 립 | +| 13 | 90← | 90 | (90,72)→(0,72) | 하단 플랜지 | +| 14 | 12↑ | 12 | (0,72)→(0,60) | 좌측벽 하부 | +| 15 | 10→ | 10 | (0,60)→(10,60) | 립 (Seg1과 겹침) | + +> **핵심**: 내부 개구부(스텝 구조)가 **하부**(y=51~66)에 위치한다. 상부(y=21)에는 넓은 78mm 선반, 하부에는 43mm 선반 + 15-20-15-15 스텝이 배치된다. + +### 2.3 ① 마감재 절곡 (SUS 1.2T) + +**상단**: `10←lip + 13↓tab + 120→body + 25↓ + 15←` +**하단**: `10←lip + 13↑tab + 120→body + 25↑ + 19← + 14↓ + 15←` + +### 2.4 ④ 벽연형 (EGI 1.55T) + +ㄷ자 형태: `30(깊이) × 45(높이)`, 우측 열림. 본체 좌측에 위치, 세로 중앙 정렬. + +--- + +## 3. 2D 평면도 (SVG) 구현 + +### 3.1 좌표계 + +``` +X축: 깊이 (LEFT=벽/방화벽 → RIGHT=개구부/실내) +Y축: 높이 (TOP → BOTTOM) +스케일: sc = 4 (1mm = 4px) +``` + +### 3.2 본체 위치 계산 + +```javascript +const ox2 = w4x + w4depth + (7 + 0.75) * sc; +// ④ 벽연형 끝에서 7mm + 반두께(0.75mm) 간격 +``` + +### 3.3 색상 체계 + +| 부재 | 색상 | HEX | +|------|------|-----| +| ② 본체 EGI | 회색 | `#4b5563` (fill) / `#94a3b8` (stroke) | +| ① 마감재 SUS | 빨간색 | `#ef4444` | +| ④ 벽연형 EGI | 갈색 | `#8b6c5c` | + +### 3.4 치수 라벨 + +각 부재의 절곡 치수를 **해당 부재 색상**으로 표시한다: +- ② 본체: 회색(`#94a3b8`)으로 14개 세그먼트 치수 +- ① 마감재: 빨간색(`#ef4444`)으로 상/하단 각 7개 치수 +- ④ 벽연형: 갈색(`#8b6c5c`)으로 30 × 45 + +--- + +## 4. 3D 렌더링 (Three.js) 구현 + +### 4.1 좌표계 매핑 + +``` +2D 평면도 → 3D Shape (ExtrudeGeometry) +────────────────────────────────────────────── +plan_y (높이 0→72) → X (0 → rw=75, 중앙 정렬) +plan_x (깊이 0→90) → Y (반전: Y = bYO + 90 - plan_x) + Y=0: 개구부/실내, Y=rd: 벽 +``` + +### 4.2 본체 세그먼트 변환 헬퍼 + +```javascript +function bSeg(px, py, pw, ph) { + // 평면도 좌표 (px, py, pw, ph) → THREE.Shape 좌표 + const x1 = bXO + py, x2 = bXO + py + ph; + const y1 = bYO + 90 - px - pw, y2 = bYO + 90 - px; + const s = new THREE.Shape(); + s.moveTo(x1,y1); s.lineTo(x2,y1); + s.lineTo(x2,y2); s.lineTo(x1,y2); s.lineTo(x1,y1); + return s; +} +``` + +### 4.3 재질 + +| 부재 | Material | 속성 | +|------|----------|------| +| ② 본체 / ④ 벽연형 | `MeshStandardMaterial` | color: `0x64748b`, metalness: 0.5, roughness: 0.4 | +| ① 마감재 SUS | `MeshStandardMaterial` | color: `0x9ca3af`, metalness: 0.7, roughness: 0.25 | + +### 4.4 배치 + +```javascript +// 좌측 레일: 셔터 중심에서 -hw 위치 +railGroupL.position.set(-hw - rw/2, 0, rd/2); +// 우측 레일: scale.x = -1 (좌우 반전) +railGroupR.position.set(hw + rw/2, 0, rd/2); +``` + +--- + +## 5. 스크린형 가이드레일 + +### 5.1 부재 구성 + +| 부재 | 색상 | 절곡 치수 | +|------|------|----------| +| ② 가이드레일 EGI | 파란색 `#3b82f6` | lip-flange-sideWall-backWall (가변) | +| ① 마감재 SUS | 빨간색 `#ef4444` | 10-11-110-30-15-15-15 (J-hook) | +| ③ C형 EGI | 초록색 `#22c55e` | 30-45-30 | +| ④ D형 EGI | 주황색 `#f97316` | 11-23-40-23-11 | + +### 5.2 치수 라벨 + +각 부재 색상에 맞춰 모든 절곡 치수를 `font-size="7"` 라벨로 표시한다. + +--- + +## 6. 소스 코드 위치 + +| 함수/섹션 | 줄 번호 (대략) | 설명 | +|----------|-------------|------| +| 스크린형 2D 평면도 | ~830~1055 | SVG 렌더링 (bodySvg, bk3Svg, bk4Svg, trim1Svg) | +| 철재스라트 2D 평면도 | ~1058~1320 | SVG 렌더링 (② 본체 15세그먼트, ①, ④) | +| `renderGrFront()` | ~1330 | 정면도 SVG | +| `createRailGroup()` | ~2639 | 3D 가이드레일 생성 (스크린/철재 분기) | +| 철재 3D 프로파일 | ~2795~2816 | `bSeg()` 헬퍼로 15세그먼트 + ④ + ① | + +--- + +## 7. 참조 자료 + +- 정석 도면: 가이드레일 본체 (EGI 1.55mm) 절곡도 +- 치수: 90(상단) - 21(상부립) - 78(선반) - 30(내부벽) - 43(선반) - 15-20-15-15(스텝) - 21(하부립) - 90(하단) - 12(좌측하) - 10(립) - 60(좌측상) + +--- + +## 관련 문서 + +- [R&D 메뉴 README](README.md) — R&D 전체 메뉴 구조 +- [기획디자인 스토리보드](planning-design.md) — 블록 에디터 + +--- + +**최종 업데이트**: 2026-03-16 From 054d5d23f71213a92f8cab581683660779a1c94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 21:27:17 +0900 Subject: [PATCH 08/19] =?UTF-8?q?docs:=20[order]=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=EC=83=9D=EC=82=B0=EA=B4=80=EB=A6=AC=20API=20=EB=AA=85=EC=84=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - frontend/api-specs/stock-production-api.md 신규 작성 - INDEX.md에 재고생산관리 API 문서 등록 --- INDEX.md | 2 + frontend/api-specs/stock-production-api.md | 349 +++++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 frontend/api-specs/stock-production-api.md diff --git a/INDEX.md b/INDEX.md index 46bab30..c9fe3be 100644 --- a/INDEX.md +++ b/INDEX.md @@ -24,6 +24,7 @@ | 견적관리 | `features/quotes/README.md` | 견적 시스템, BOM 계산 | | 급여관리 API | `frontend/api-specs/payroll-api.md` | 급여관리 API 전체 명세 (18개 엔드포인트) | | 바로빌 회계 API | `frontend/api-specs/barobill-api.md` | 카드/은행/홈택스 REST API (42개 엔드포인트) | +| 재고생산관리 API | `frontend/api-specs/stock-production-api.md` | 재고생산 API 명세 (기존 수주 API + STOCK 타입) | | 결재관리 | `dev/dev_plans/approval-system-unification-plan.md` | MNG→API 결재 통합 계획 | | API 품질 | `system/api-code-quality-audit.md` | 정석 패턴 R1~R6, 안티패턴, 보안, 체크리스트 | | API 학습 | `dev/guides/api-request-lifecycle.md` | Client API로 배우는 요청 생명주기 8단계 | @@ -262,6 +263,7 @@ DB 도메인별: | [barobill-api.md](frontend/api-specs/barobill-api.md) | 바로빌 회계 데이터 API 명세 (42개 엔드포인트) | | [equipment-api.md](requests/equipment-frontend-request.md) | 설비관리 React 프론트엔드 구현 요청 (26개 엔드포인트 + 화면 가이드) | | [vehicle-api.md](frontend/api-specs/vehicle-api.md) | 차량관리 API 명세 (20개 엔드포인트: 차량목록, 차량일지, 정비이력, 사진) | +| [stock-production-api.md](frontend/api-specs/stock-production-api.md) | 재고생산관리 API 명세 (기존 수주 API + STOCK 타입) | | [vehicle-react-implementation.md](plans/vehicle-react-implementation.md) | 차량관리 React 구현 요청서 (3개 메뉴, 컴포넌트 구조, 타입 정의) | ### frontend/integration/ — 프론트엔드 개발 가이드 diff --git a/frontend/api-specs/stock-production-api.md b/frontend/api-specs/stock-production-api.md new file mode 100644 index 0000000..c793928 --- /dev/null +++ b/frontend/api-specs/stock-production-api.md @@ -0,0 +1,349 @@ +# 재고생산관리 API 명세 + +> **작성일**: 2026-03-16 +> **상태**: 설계 확정 +> **대상**: React 프론트엔드 개발자 + +--- + +## 1. 개요 + +### 1.1 목적 + +재고생산관리는 **수주 없이 재고 목적으로 생산**하는 경우를 위한 기능이다. +기존 수주관리(Order) API를 **그대로 사용**하되, `order_type_code: 'STOCK'`으로 구분한다. + +### 1.2 핵심 원칙 + +- 별도 API 엔드포인트 없음 — 기존 `/api/v1/orders` 사용 +- `order_type_code` 필터로 수주(`ORDER`)와 재고생산(`STOCK`)을 분리 +- 화면은 수주관리를 **단순화**한 형태 (거래처, 견적, 배송, 할인 등 불필요) +- 하류 시스템(작업지시, 생산, 출하)은 **동일하게 동작** + +--- + +## 2. API 엔드포인트 + +> **기본 URL**: `/api/v1/orders` +> **인증**: `Bearer Token` (Sanctum) + `API Key` + +### 2.1 목록 조회 + +``` +GET /api/v1/orders?order_type=STOCK +``` + +| 파라미터 | 타입 | 필수 | 설명 | +|----------|------|:----:|------| +| `order_type` | string | ✅ | `STOCK` 고정 | +| `page` | int | - | 페이지 (기본: 1) | +| `size` | int | - | 페이지 크기 (기본: 20) | +| `q` | string | - | 검색어 (생산번호, 메모) | +| `status` | string | - | 상태 필터 (`DRAFT`, `CONFIRMED` 등) | +| `date_from` | date | - | 등록일 시작 | +| `date_to` | date | - | 등록일 종료 | + +**응답 예시**: + +```json +{ + "success": true, + "message": "message.order.fetched", + "data": { + "data": [ + { + "id": 42, + "order_no": "STK202603160001", + "order_type_code": "STOCK", + "status_code": "DRAFT", + "quantity": "100.0000", + "memo": "봄 시즌 대비 재고 확보", + "options": { + "production_reason": "시즌 대비", + "target_stock_qty": 500 + }, + "items": [ + { + "id": 1, + "item_name": "25mm 알루미늄 블라인드", + "specification": "W1000 x H2000", + "quantity": "100.0000", + "unit_price": "15000.00" + } + ], + "created_at": "2026-03-16T10:30:00Z" + } + ], + "current_page": 1, + "last_page": 1, + "total": 1 + } +} +``` + +--- + +### 2.2 통계 조회 + +``` +GET /api/v1/orders/stats?order_type=STOCK +``` + +| 파라미터 | 타입 | 필수 | 설명 | +|----------|------|:----:|------| +| `order_type` | string | ✅ | `STOCK` 고정 | + +**응답 예시**: + +```json +{ + "success": true, + "data": { + "total": 15, + "draft": 3, + "confirmed": 5, + "in_progress": 4, + "completed": 2, + "cancelled": 1, + "total_amount": 0, + "confirmed_amount": 0 + } +} +``` + +--- + +### 2.3 생성 + +``` +POST /api/v1/orders +``` + +**요청 본문**: + +```json +{ + "order_type_code": "STOCK", + "memo": "봄 시즌 대비 재고 확보", + "options": { + "production_reason": "시즌 대비", + "target_stock_qty": 500 + }, + "items": [ + { + "item_id": 10, + "item_name": "25mm 알루미늄 블라인드", + "specification": "W1000 x H2000", + "quantity": 100, + "unit_price": 0, + "unit": "EA" + } + ] +} +``` + +**필수 필드**: + +| 필드 | 타입 | 필수 | 설명 | +|------|------|:----:|------| +| `order_type_code` | string | ✅ | `STOCK` 고정 | +| `items` | array | ✅ | 생산할 품목 목록 | +| `items[].item_name` | string | ✅ | 품목명 | +| `items[].quantity` | number | ✅ | 수량 | +| `items[].unit_price` | number | ✅ | 단가 (재고생산은 `0` 가능) | + +**선택 필드**: + +| 필드 | 타입 | 설명 | +|------|------|------| +| `memo` | string | 비고 | +| `options.production_reason` | string | 생산 사유 | +| `options.target_stock_qty` | number | 목표 재고 수량 | +| `items[].item_id` | int | 품목 마스터 ID | +| `items[].specification` | string | 규격 | +| `items[].unit` | string | 단위 | + +**사용하지 않는 필드** (수주 전용): + +``` +❌ client_id, client_name, client_contact → 거래처 없음 +❌ site_name → 현장 없음 +❌ quote_id → 견적 없음 +❌ delivery_date, delivery_method_code → 납기/배송 불필요 +❌ discount_rate, discount_amount → 할인 없음 +❌ sales_recognition → 매출 인식 불필요 +❌ options.shipping_cost_code → 운임비용 불필요 +❌ options.receiver, receiver_contact → 수신자 불필요 +❌ options.shipping_address → 배송지 불필요 +``` + +> **자동 생성**: `order_no`는 `STK{YYYYMMDD}{NNNN}` 형식으로 서버에서 자동 생성 + +--- + +### 2.4 단건 조회 + +``` +GET /api/v1/orders/{id} +``` + +기존 수주 조회와 동일. `order_type_code`가 `STOCK`인지 프론트에서 확인. + +--- + +### 2.5 수정 + +``` +PUT /api/v1/orders/{id} +``` + +생성과 동일한 필드 사용. `order_type_code`는 변경하지 않는다. + +--- + +### 2.6 삭제 + +``` +DELETE /api/v1/orders/{id} +``` + +기존 수주 삭제와 동일. + +--- + +### 2.7 상태 변경 + +``` +PATCH /api/v1/orders/{id}/status +``` + +```json +{ + "status": "CONFIRMED" +} +``` + +**상태 흐름** (수주와 동일): + +``` +DRAFT → CONFIRMED → IN_PROGRESS → COMPLETED + └─────→ CANCELLED ──→ DRAFT (복구) +``` + +> **재고생산 특수**: `CONFIRMED` 상태 진입 시 매출 자동 생성이 **발생하지 않음** + +--- + +### 2.8 생산지시 생성 + +``` +POST /api/v1/orders/{id}/production-order +``` + +수주와 동일하게 작업지시(WorkOrder)를 생성한다. + +```json +{ + "process_ids": [1, 2, 3], + "priority": "normal", + "scheduled_date": "2026-03-20", + "memo": "재고생산 작업" +} +``` + +--- + +## 3. 채번 규칙 + +| 주문 유형 | 접두사 | 예시 | +|----------|--------|------| +| 수주 (ORDER) | `ORD` | `ORD202603160001` | +| 재고생산 (STOCK) | `STK` | `STK202603160001` | + +--- + +## 4. 화면 설계 가이드 + +### 4.1 수주관리 화면 vs 재고생산관리 화면 + +``` +┌─ 수주관리 ──────────────────┐ ┌─ 재고생산관리 ───────────────┐ +│ │ │ │ +│ [거래처 정보] │ │ [생산 정보] │ +│ - 거래처 선택 │ │ - 생산 사유 (텍스트) │ +│ - 현장명 │ │ - 목표 재고 수량 │ +│ - 배송/납기일 │ │ - 비고 │ +│ - 운임비용 │ │ │ +│ - 견적 연결 │ ├──────────────────────────────┤ +│ - 할인 │ │ [품목 목록] │ +│ - 매출 인식 방식 │ │ - 품목 선택 │ +│ │ │ - 수량 │ +│ [개소/구역 (OrderNode)] │ │ - 규격 │ +│ - N-depth 트리 구조 │ │ │ +│ │ │ (단가/금액/할인 불필요) │ +│ [품목 목록] │ │ (개소/구역 불필요) │ +│ - 품목, 수량, 단가, 할인 │ │ │ +│ - floor_code, symbol_code │ └──────────────────────────────┘ +│ - 금액 계산 │ +│ │ +└──────────────────────────────┘ +``` + +### 4.2 목록 페이지 표시 컬럼 + +| 컬럼 | 설명 | +|------|------| +| 생산번호 | `order_no` (STK...) | +| 상태 | `status_code` (상태 배지) | +| 품목 | 대표 품목명 + (외 N건) | +| 수량 | 총 수량 | +| 생산 사유 | `options.production_reason` | +| 등록일 | `created_at` | + +### 4.3 상태 배지 색상 (수주와 동일) + +| 상태 | 라벨 | 색상 | +|------|------|------| +| `DRAFT` | 등록 | gray | +| `CONFIRMED` | 확정 | blue | +| `IN_PROGRESS` | 진행중 | yellow | +| `IN_PRODUCTION` | 생산중 | orange | +| `PRODUCED` | 생산완료 | green | +| `COMPLETED` | 완료 | green | +| `CANCELLED` | 취소 | red | + +--- + +## 5. 구현 체크리스트 + +### 프론트엔드 (React) + +- [ ] 재고생산관리 메뉴/라우트 추가 +- [ ] 목록 페이지: `GET /api/v1/orders?order_type=STOCK` +- [ ] 통계 카드: `GET /api/v1/orders/stats?order_type=STOCK` +- [ ] 생성 폼: `order_type_code: 'STOCK'` 필수 전송 +- [ ] 상세/수정 페이지: 수주 화면 단순화 (거래처/견적/배송/할인 제거) +- [ ] 생산지시 생성 버튼: `POST /api/v1/orders/{id}/production-order` +- [ ] 상태 변경: `PATCH /api/v1/orders/{id}/status` + +### 백엔드 (API) — 완료 + +- [x] `Order::TYPE_STOCK = 'STOCK'` 상수 추가 +- [x] StoreOrderRequest에 `STOCK` validation 추가 +- [x] UpdateOrderRequest에 `STOCK` validation 추가 +- [x] 재고생산 채번: `STK{YYYYMMDD}{NNNN}` +- [x] stats()에 `order_type` 필터 추가 +- [x] STOCK 타입 확정 시 매출 자동 생성 스킵 +- [x] options에 `production_reason`, `target_stock_qty` 필드 추가 + +--- + +## 관련 문서 + +- 수주관리: 기존 수주 API는 동일 엔드포인트 (`/api/v1/orders`) +- 작업지시: `POST /api/v1/orders/{id}/production-order` +- 품목관리: `rules/item-policy.md` + +--- + +**최종 업데이트**: 2026-03-16 From 6eb4f96eaad10216081a0ae48ef7af2fb8480b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 21:27:21 +0900 Subject: [PATCH 09/19] =?UTF-8?q?docs:=20CLAUDE.md=20=EC=9A=A9=EC=96=B4=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=20=EC=84=B9=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 서비스(API+React) / 백오피스(MNG) 명칭 정의 --- CLAUDE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index dd62af6..c04a166 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -818,7 +818,6 @@ sudo supervisorctl restart sam-mng-worker:* | Skill | 설명 | |-------|------| | `pptx-skill` | PowerPoint 생성 | -| `ppt-auto-generator` | 마크다운/텍스트에서 PPT 생성 | | `pdf-template-skill` | PDF 템플릿 분석/생성 | | `text-analyzer-skill` | 텍스트 분석 및 PDF 구조 매핑 | | `proposal-skill` | 제안서 생성 | From 3529feeb70abf2a23ce10221385672ee1ee34244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 21:41:42 +0900 Subject: [PATCH 10/19] =?UTF-8?q?docs:=20[sales]=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=EC=83=9D=EC=82=B0=EA=B4=80=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=20=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - features/sales/stock-production.md 신규 작성 - INDEX.md에 문서 등록 --- INDEX.md | 1 + features/sales/stock-production.md | 113 +++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 features/sales/stock-production.md diff --git a/INDEX.md b/INDEX.md index c9fe3be..5b7f3b7 100644 --- a/INDEX.md +++ b/INDEX.md @@ -165,6 +165,7 @@ DB 도메인별: | [ai/README.md](features/ai/README.md) | AI 분석 | | [card-vehicle/README.md](features/card-vehicle/README.md) | 법인카드·차량 | | [settlement/README.md](features/settlement/README.md) | 정산 | +| [sales/stock-production.md](features/sales/stock-production.md) | 재고생산관리 (내부 오더 방식, 수주 테이블 공유) | | [sales/demo-tenant-policy.md](features/sales/demo-tenant-policy.md) | 영업파트너 데모 테넌트 정책 (3-Tier 전략) | | [sales/demo-tenant-usage-guide.md](features/sales/demo-tenant-usage-guide.md) | 데모 테넌트 사용 가이드 (영업파트너/관리자용) | | [barobill-kakaotalk/README.md](features/barobill-kakaotalk/README.md) | 바로빌 카카오톡 | diff --git a/features/sales/stock-production.md b/features/sales/stock-production.md new file mode 100644 index 0000000..cf8d0d1 --- /dev/null +++ b/features/sales/stock-production.md @@ -0,0 +1,113 @@ +# 재고생산관리 + +> **작성일**: 2026-03-16 +> **상태**: 설계 확정 + +--- + +## 1. 개요 + +### 1.1 목적 + +수주 없이 **재고 목적으로 생산**하는 경우를 관리하는 기능이다. + +SAM 시스템은 `수주 → 작업지시 → 생산`의 흐름으로 동작하므로, 수주가 없으면 작업지시를 생성할 수 없다. 재고생산관리는 **내부 오더(Internal Order)** 개념으로, 재고생산도 일종의 수주로 취급하여 기존 생산 흐름을 그대로 활용한다. + +### 1.2 핵심 원칙 + +- 기존 `orders` 테이블을 **공유**한다 (별도 테이블 없음) +- `order_type_code = 'STOCK'`으로 일반 수주(`ORDER`)와 구분한다 +- 하류 시스템(작업지시, 생산, 출하, 품질검사)은 **변경 없이 동작**한다 +- 화면은 수주관리를 **단순화**한 형태이다 + +--- + +## 2. 수주관리와의 관계 + +### 2.1 테이블 공유 구조 + +``` +orders 테이블 +├── order_type_code = 'ORDER' → 수주관리 (거래처 주문) +├── order_type_code = 'PURCHASE' → 발주관리 +└── order_type_code = 'STOCK' → 재고생산관리 (내부 오더) +``` + +### 2.2 기능 비교 + +| 항목 | 수주관리 (ORDER) | 재고생산관리 (STOCK) | +|------|-----------------|---------------------| +| 거래처 | ✅ 필수 | ❌ 불필요 | +| 견적 연결 | ✅ 가능 | ❌ 불필요 | +| 현장/납기/배송 | ✅ 사용 | ❌ 불필요 | +| 할인 | ✅ 적용 | ❌ 불필요 | +| 금액 계산 | ✅ 자동 | ❌ 불필요 (단가 0 가능) | +| 매출 인식 | ✅ 자동/수동 | ❌ 생성 안 함 | +| 개소/구역 (OrderNode) | ✅ N-depth 트리 | ❌ 불필요 | +| 품목 등록 | ✅ 상세 | ✅ 품목 + 수량만 | +| 작업지시 생성 | ✅ 동일 | ✅ 동일 | +| 생산/출하/품질검사 | ✅ 동일 | ✅ 동일 | +| 채번 접두사 | `ORD` | `STK` | + +### 2.3 재고생산 전용 필드 + +`orders.options` JSON에 재고생산 전용 속성을 저장한다: + +| 필드 | 타입 | 설명 | +|------|------|------| +| `production_reason` | string | 생산 사유 (예: "시즌 대비", "안전재고 확보") | +| `target_stock_qty` | number | 목표 재고 수량 | + +--- + +## 3. 업무 흐름 + +``` +재고생산 등록 (DRAFT) + │ order_type_code = 'STOCK' + │ 품목 + 수량만 입력 + ↓ +재고생산 확정 (CONFIRMED) + │ ※ 매출 생성 안 함 (일반 수주와 차이) + ↓ +생산지시 생성 (WorkOrder) + │ 기존 수주→생산지시 흐름 동일 + ↓ +생산 실행 → 품질검사 → 출하 + │ 기존 생산관리 흐름 동일 + ↓ +완료 (COMPLETED) +``` + +--- + +## 4. 메뉴 위치 + +``` +판매관리 +├── 거래처관리 +├── 견적관리 +├── 수주관리 +├── 재고생산관리 ← 신규 +└── 단가관리 +``` + +--- + +## 5. API + +기존 수주관리 API를 그대로 사용한다. `order_type=STOCK` 파라미터로 필터링한다. + +> **상세 API 명세**: `frontend/api-specs/stock-production-api.md` + +--- + +## 관련 문서 + +- [재고생산관리 API 명세](../../frontend/api-specs/stock-production-api.md) +- [수주 DB 스키마](../../system/database/sales.md) +- [생산 DB 스키마](../../system/database/production.md) + +--- + +**최종 업데이트**: 2026-03-16 From cd3ee4e817b19064410ab6184baf07f69689e30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 22:24:24 +0900 Subject: [PATCH 11/19] =?UTF-8?q?docs:=20[order]=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=EC=83=9D=EC=82=B0=20API=20=EC=8A=A4=ED=8E=99=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 생산지시 자동 처리 항목 추가 (절곡 공정, project_name, scheduled_date) - 프론트엔드 체크리스트 업데이트 --- frontend/api-specs/stock-production-api.md | 28 ++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/frontend/api-specs/stock-production-api.md b/frontend/api-specs/stock-production-api.md index c793928..b6212c7 100644 --- a/frontend/api-specs/stock-production-api.md +++ b/frontend/api-specs/stock-production-api.md @@ -167,7 +167,7 @@ POST /api/v1/orders ``` ❌ client_id, client_name, client_contact → 거래처 없음 -❌ site_name → 현장 없음 +❌ site_name → API가 '재고생산'으로 자동 설정 ❌ quote_id → 견적 없음 ❌ delivery_date, delivery_method_code → 납기/배송 불필요 ❌ discount_rate, discount_amount → 할인 없음 @@ -240,19 +240,30 @@ DRAFT → CONFIRMED → IN_PROGRESS → COMPLETED POST /api/v1/orders/{id}/production-order ``` -수주와 동일하게 작업지시(WorkOrder)를 생성한다. +재고생산은 **절곡 공정이 자동 선택**된다 (API에서 처리). `process_ids` 전달 불필요. ```json { - "process_ids": [1, 2, 3], "priority": "normal", - "scheduled_date": "2026-03-20", "memo": "재고생산 작업" } ``` --- +**API 자동 처리 항목** (STOCK 타입): + +| 항목 | 일반 수주 | 재고생산 (자동) | +|------|----------|---------------| +| 현장명 (`project_name`) | `site_name` 또는 `client_name` | `'재고생산'` 고정 | +| 공정 | BOM item_id → process_items 매칭 | **절곡 공정 자동 선택** | +| 작업예정일 (`scheduled_date`) | `delivery_date` | **생성일 (now())** | +| 매출 생성 | `sales_recognition` 정책 적용 | **생성 안 함** | + +> 프론트에서 `process_ids`, `scheduled_date`를 전달하지 않아도 API가 자동 처리 + +--- + ## 3. 채번 규칙 | 주문 유형 | 접두사 | 예시 | @@ -326,6 +337,11 @@ POST /api/v1/orders/{id}/production-order - [ ] 생산지시 생성 버튼: `POST /api/v1/orders/{id}/production-order` - [ ] 상태 변경: `PATCH /api/v1/orders/{id}/status` +### 프론트엔드 — 생산지시 목록 수정 + +- [ ] 생산지시 목록에서 `order_type_code`가 `STOCK`인 건: 납기 컬럼 `-` 표시 +- [ ] 생산지시 생성 UI: STOCK 수주는 공정 선택 불필요 (API가 절곡 자동 선택) + ### 백엔드 (API) — 완료 - [x] `Order::TYPE_STOCK = 'STOCK'` 상수 추가 @@ -335,6 +351,10 @@ POST /api/v1/orders/{id}/production-order - [x] stats()에 `order_type` 필터 추가 - [x] STOCK 타입 확정 시 매출 자동 생성 스킵 - [x] options에 `production_reason`, `target_stock_qty` 필드 추가 +- [x] store()에서 STOCK 타입 `site_name = '재고생산'` 자동 설정 +- [x] createProductionOrder()에서 STOCK: `project_name = '재고생산'` 고정 +- [x] createProductionOrder()에서 STOCK: 절곡 공정 자동 선택 +- [x] createProductionOrder()에서 STOCK: `scheduled_date = now()` 자동 설정 --- From 029d4b6f02b4128265cc564ae9feb450472952da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 22:28:49 +0900 Subject: [PATCH 12/19] =?UTF-8?q?docs:=20[order]=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=EC=83=9D=EC=82=B0=EA=B4=80=EB=A6=AC=20=EA=B0=9C=EB=B0=9C?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20+=20React=20=EA=B5=AC=ED=98=84=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dev/changes/20260316_stock_production_order.md: 변경이력 - plans/stock-production-react-request.md: 프론트 개발자 전달 문서 - INDEX.md 등록 --- INDEX.md | 2 + .../20260316_stock_production_order.md | 139 +++++++ plans/stock-production-react-request.md | 341 ++++++++++++++++++ 3 files changed, 482 insertions(+) create mode 100644 dev/changes/20260316_stock_production_order.md create mode 100644 plans/stock-production-react-request.md diff --git a/INDEX.md b/INDEX.md index 5b7f3b7..6fde46d 100644 --- a/INDEX.md +++ b/INDEX.md @@ -234,6 +234,7 @@ DB 도메인별: | [20260314_api_quality_improvement_deploy.md](dev/changes/20260314_api_quality_improvement_deploy.md) | API 품질 개선 배포 — 테스트 56개 + N+1 최적화 3건 (근거 문서 포함) | | [20260315_eval_removal_safe_math_evaluator.md](dev/changes/20260315_eval_removal_safe_math_evaluator.md) | API 보안 개선 — eval() 3건 제거, SafeMathEvaluator 도입 | | [20260316_sales_policy_changes.md](changes/20260316_sales_policy_changes.md) | 영업 정책 변경 — 수당 구조 개편 및 무료 체험 폐지 | +| [20260316_stock_production_order.md](dev/changes/20260316_stock_production_order.md) | 재고생산관리 기능 추가 (STOCK 타입, 절곡 공정 자동, 생산지시 연동) | --- @@ -266,6 +267,7 @@ DB 도메인별: | [vehicle-api.md](frontend/api-specs/vehicle-api.md) | 차량관리 API 명세 (20개 엔드포인트: 차량목록, 차량일지, 정비이력, 사진) | | [stock-production-api.md](frontend/api-specs/stock-production-api.md) | 재고생산관리 API 명세 (기존 수주 API + STOCK 타입) | | [vehicle-react-implementation.md](plans/vehicle-react-implementation.md) | 차량관리 React 구현 요청서 (3개 메뉴, 컴포넌트 구조, 타입 정의) | +| [stock-production-react-request.md](plans/stock-production-react-request.md) | 재고생산관리 React 구현 요청서 (수주 화면 단순화, API 스펙 포함) | ### frontend/integration/ — 프론트엔드 개발 가이드 diff --git a/dev/changes/20260316_stock_production_order.md b/dev/changes/20260316_stock_production_order.md new file mode 100644 index 0000000..7a72b1f --- /dev/null +++ b/dev/changes/20260316_stock_production_order.md @@ -0,0 +1,139 @@ +# 재고생산관리 기능 추가 + +**날짜:** 2026-03-16 +**작업자:** Claude Code + +--- + +## 변경 개요 + +수주 없이 재고 목적으로 생산하는 경우를 관리하는 **재고생산관리** 기능을 추가했다. +기존 `orders` 테이블을 공유하며, `order_type_code = 'STOCK'`으로 일반 수주와 구분한다. + +### 설계 원칙 + +- **내부 오더(Internal Order)** 패턴 — 재고생산도 수주로 취급 +- 기존 테이블/API 재사용 — 별도 테이블/엔드포인트 없음 +- 하류 시스템(작업지시, 생산, 출하, 품질검사) 변경 없이 동작 + +--- + +## 수정된 파일 + +### 커밋 1: `feat: [order] 재고생산관리(STOCK) 타입 추가` + +| 파일 | 변경 내용 | +|------|----------| +| `app/Models/Orders/Order.php` | `TYPE_STOCK = 'STOCK'` 상수 추가 | +| `app/Http/Requests/Order/StoreOrderRequest.php` | STOCK validation 허용, `production_reason`/`target_stock_qty` 옵션 추가 | +| `app/Http/Requests/Order/UpdateOrderRequest.php` | 동일 | +| `app/Services/OrderService.php` | STK 채번, stats `order_type` 필터, 매출 생성 스킵 | +| `app/Http/Controllers/Api/V1/OrderController.php` | stats에 `order_type` 파라미터 전달 | + +### 커밋 2: `feat: [order] 재고생산 생산지시 자동 처리` + +| 파일 | 변경 내용 | +|------|----------| +| `app/Services/OrderService.php` | `store()`: STOCK → `site_name='재고생산'` 자동 설정 | +| `app/Services/OrderService.php` | `createProductionOrder()`: STOCK 분기 추가 (절곡 자동, project_name, scheduled_date) | +| `lang/ko/error.php` | `bending_process_not_found` 에러 메시지 추가 | +| `lang/en/error.php` | 동일 (영문) | + +--- + +## 상세 변경 사항 + +### 1. Order 모델 — 타입 상수 + +```php +public const TYPE_ORDER = 'ORDER'; // 수주 +public const TYPE_PURCHASE = 'PURCHASE'; // 발주 +public const TYPE_STOCK = 'STOCK'; // 재고생산 (신규) +``` + +### 2. 채번 규칙 + +| 타입 | 접두사 | 형식 | 예시 | +|------|--------|------|------| +| ORDER | `ORD` | `ORD{YYYYMMDD}{NNNN}` | `ORD202603160001` | +| STOCK | `STK` | `STK{YYYYMMDD}{NNNN}` | `STK202603160001` | + +### 3. store() — STOCK 자동 처리 + +```php +if ($isStock) { + $data['site_name'] = '재고생산'; +} +``` + +### 4. createProductionOrder() — STOCK 분기 + +| 항목 | ORDER (기존) | STOCK (신규) | +|------|-------------|-------------| +| 공정 매칭 | BOM item_id → process_items 매핑 | **절곡 공정 직접 할당** (BOM 스킵) | +| project_name | `order.site_name ?? client_name` | `'재고생산'` 고정 | +| scheduled_date | `order.delivery_date` | `now()` | +| 매출 생성 | `sales_recognition` 정책 적용 | **생성 안 함** | + +절곡 공정 조회: +```php +$bendingProcess = Process::where('tenant_id', $tenantId) + ->where('process_name', '절곡') + ->where('is_active', true) + ->first(); +``` + +### 5. stats() — order_type 필터 + +```php +public function stats(?string $orderType = null): array +``` + +`GET /api/v1/orders/stats?order_type=STOCK` 으로 재고생산 전용 통계 조회 가능. + +### 6. 매출 생성 스킵 + +```php +if ($status === Order::STATUS_CONFIRMED + && $order->order_type_code !== Order::TYPE_STOCK // STOCK 제외 + && $order->shouldCreateSaleOnConfirm()) { +``` + +--- + +## 영향범위 분석 + +| 영역 | 영향 | 이유 | +|------|------|------| +| 기존 수주(ORDER) | ❌ 없음 | `$isStock` 조건 분기, else 블록에서 기존 코드 그대로 실행 | +| 기존 발주(PURCHASE) | ❌ 없음 | 동일 | +| 작업지시(WorkOrder) | ✅ 정상 동작 | `sales_order_id` FK로 연결, 절곡 공정 할당됨 | +| 생산/품질검사 | ❌ 없음 | WorkOrder 기반 하류 시스템, Order 타입 무관 | +| 출하(Shipment) | ❌ 없음 | WorkOrder 참조, Order.site_name 미사용 | +| 캘린더 | ✅ 표시됨 | `project_name='재고생산'`, `scheduled_date=now()` | +| 생산지시 목록 | ✅ 표시됨 | `site_name='재고생산'`으로 현장명 표시 | + +--- + +## 테스트 체크리스트 + +- [x] STOCK 수주 생성 → `order_no` STK 접두사 확인 +- [x] STOCK 수주 생성 → `site_name='재고생산'` 자동 설정 확인 +- [ ] STOCK 수주 확정 → 매출 자동 생성 안 됨 확인 +- [ ] STOCK 생산지시 생성 → 절곡 공정 자동 선택 확인 +- [ ] STOCK 생산지시 생성 → `project_name='재고생산'` 확인 +- [ ] STOCK 생산지시 생성 → `scheduled_date=today` 확인 +- [ ] 기존 ORDER 수주 생산지시 → 기존 BOM 매칭 정상 동작 확인 +- [ ] 생산지시 목록에서 STOCK 건 표시 확인 + +--- + +## 관련 문서 + +- [재고생산관리 기능 설명](../../features/sales/stock-production.md) +- [재고생산관리 API 명세](../../frontend/api-specs/stock-production-api.md) +- [프론트엔드 구현 요청서](../../frontend/requests/stock-production-react-request.md) + +--- + +**최종 업데이트**: 2026-03-16 diff --git a/plans/stock-production-react-request.md b/plans/stock-production-react-request.md new file mode 100644 index 0000000..d8eedbe --- /dev/null +++ b/plans/stock-production-react-request.md @@ -0,0 +1,341 @@ +# 재고생산관리 React 구현 요청서 + +> **작성일**: 2026-03-16 +> **요청자**: R&D 실장 +> **대상**: 프론트엔드 개발자 +> **우선순위**: 🟡 중요 +> **API 상태**: ✅ 구현 완료 (개발서버 배포됨) + +--- + +## 1. 개요 + +수주 없이 **재고 목적으로 생산**하는 경우를 관리하는 메뉴를 추가한다. +기존 수주관리 API를 그대로 사용하며, `order_type_code = 'STOCK'`으로 구분한다. + +### 1.1 구현 대상 + +| 메뉴 | 위치 | 설명 | 난이도 | +|------|------|------|:------:| +| **재고생산관리** | 판매관리 > 재고생산관리 | 재고생산 등록/목록/상세/수정/삭제 | **낮음** (수주 화면 복제 후 단순화) | + +### 1.2 참고 문서 + +| 문서 | 경로 | 용도 | +|------|------|------| +| **API 명세** (필독) | `docs/frontend/api-specs/stock-production-api.md` | 전체 엔드포인트, 요청/응답 형식 | +| 기능 설명 | `docs/features/sales/stock-production.md` | 비즈니스 개념, 수주와의 관계 | + +### 1.3 핵심 원칙 + +``` +✅ 수주관리 화면을 복제한 뒤 불필요한 필드 제거 +✅ API는 기존 /api/v1/orders 그대로 사용 (order_type=STOCK 필터) +✅ 생성 시 order_type_code: 'STOCK' 필수 전송 +❌ 별도 API 엔드포인트 없음 +❌ 거래처, 견적, 배송, 할인, 개소(Node) 관련 UI 불필요 +``` + +--- + +## 2. 파일 구조 (제안) + +기존 수주관리 구조를 따른다. + +``` +src/ +├── app/[locale]/(protected)/sales/order-management-sales/ +│ └── stocks/ # 재고생산관리 (신규) +│ ├── page.tsx # 목록 페이지 +│ └── [id]/ +│ ├── page.tsx # 상세 페이지 +│ └── production-order/ +│ └── page.tsx # 생산지시 생성 페이지 +│ +├── components/orders/ +│ └── StockProductionRegistration.tsx # 재고생산 등록/수정 폼 (신규) +│ └── (기존 actions.ts에 함수 추가 또는 별도 stockActions.ts) +``` + +> **대안**: `stocks/` 대신 기존 수주관리 페이지에서 `order_type` 탭으로 분리하는 방식도 가능. 실장 판단에 따름. + +--- + +## 3. 화면별 구현 가이드 + +### 3.1 재고생산 목록 페이지 + +**경로**: `/sales/order-management-sales/stocks` + +**수주관리 목록 페이지를 복제**한 뒤 다음을 변경: + +| 항목 | 수주관리 (현재) | 재고생산 (변경) | +|------|---------------|---------------| +| 페이지 제목 | 수주관리 | **재고생산관리** | +| API 호출 | `getOrders()` | `getOrders({ order_type: 'STOCK' })` | +| 통계 API | `getOrderStats()` | `getOrderStats({ order_type: 'STOCK' })` → `GET /api/v1/orders/stats?order_type=STOCK` | +| 표시 컬럼 | 수주번호, 거래처, 현장명, 수량, 납기, 금액... | **생산번호, 품목, 수량, 생산사유, 등록일, 상태** | +| 통계 카드 | 이번달 수주금액, 분할대기... | 전체, 등록, 확정, 진행중, 완료 (또는 간소화) | + +**목록 테이블 컬럼**: + +| 컬럼 | 데이터 | 비고 | +|------|--------|------| +| 번호 | 글로벌 인덱스 | | +| 생산번호 | `order_no` (STK...) | 배지 스타일 | +| 품목 | 대표 `items[0].item_name` + (외 N건) | | +| 수량 | 총 수량 | | +| 생산 사유 | `options.production_reason` | 없으면 `-` | +| 등록일 | `created_at` | YYYY-MM-DD | +| 상태 | `status_code` | 배지 (수주와 동일 색상) | +| 작업 | 상세보기, 삭제 | | + +**제거할 컬럼**: 거래처, 현장명, 납기, 금액 + +### 3.2 재고생산 등록/수정 폼 + +**수주 등록 폼(`OrderRegistration.tsx`)을 복제**한 뒤 다음을 변경: + +#### 제거할 섹션 + +``` +❌ 거래처 선택 (client_id, client_name, client_contact) +❌ 현장명 (site_name) — API가 '재고생산'으로 자동 설정 +❌ 견적 선택 (quote_id) / 견적에서 가져오기 +❌ 배송 정보 (delivery_date, delivery_method_code) +❌ 운임비용 (options.shipping_cost_code) +❌ 수신자 정보 (options.receiver, receiver_contact, shipping_address) +❌ 할인 (discount_rate, discount_amount) +❌ 금액 계산 (supply_amount, tax_amount, total_amount) +❌ 매출 인식 (sales_recognition) +❌ 개소/구역 (OrderNode) 트리 구조 +``` + +#### 유지할 섹션 + +``` +✅ 생산 사유 (options.production_reason) — 텍스트 입력 +✅ 목표 재고 수량 (options.target_stock_qty) — 숫자 입력 +✅ 비고 (memo) — 텍스트 입력 +✅ 품목 목록 (items[]) — 품목 선택 + 수량 입력 +``` + +#### 품목 입력 테이블 + +| 필드 | 타입 | 필수 | 비고 | +|------|------|:----:|------| +| 품목 | 품목 선택 (ItemAddDialog) | ✅ | item_id, item_name, item_code | +| 규격 | 텍스트 | - | specification | +| 수량 | 숫자 | ✅ | quantity | +| 단위 | 텍스트 | - | unit (기본: EA) | + +> **단가/금액은 불필요**하지만, API validation에서 `unit_price`가 required이므로 `0`을 전송 + +#### API 요청 예시 + +```json +{ + "order_type_code": "STOCK", + "memo": "봄 시즌 대비 재고 확보", + "options": { + "production_reason": "시즌 대비", + "target_stock_qty": 500 + }, + "items": [ + { + "item_id": 10, + "item_name": "25mm 알루미늄 블라인드", + "specification": "W1000 x H2000", + "quantity": 100, + "unit_price": 0, + "unit": "EA" + } + ] +} +``` + +### 3.3 재고생산 상세 페이지 + +**수주 상세(`OrderSalesDetailView.tsx`)를 복제**한 뒤 단순화: + +| 섹션 | 표시 | +|------|------| +| 생산번호 (order_no) | ✅ | +| 상태 (status_code) | ✅ 배지 | +| 생산 사유 | ✅ `options.production_reason` | +| 목표 재고 수량 | ✅ `options.target_stock_qty` | +| 비고 | ✅ `memo` | +| 품목 목록 | ✅ 테이블 (품목명, 규격, 수량) | +| 거래처/현장/납기/금액 | ❌ 제거 | +| 개소/구역 트리 | ❌ 제거 | + +**액션 버튼**: +- 수정 (DRAFT 상태만) +- 삭제 (DRAFT 상태만) +- 확정 (`PATCH /api/v1/orders/{id}/status` → `{ "status": "CONFIRMED" }`) +- 생산지시 생성 (CONFIRMED 상태만) → 기존 생산지시 생성 페이지 재사용 + +### 3.4 생산지시 생성 페이지 + +**기존 `[id]/production-order/page.tsx`를 재사용** 가능. + +STOCK 수주의 생산지시는 API가 자동 처리하므로 프론트에서 특별한 분기 불필요: + +| 항목 | API 자동 처리 | +|------|-------------| +| 공정 선택 | 절곡 공정 자동 선택 (process_ids 전달 불필요) | +| 현장명 | `'재고생산'` 고정 | +| 작업예정일 | `now()` 자동 (scheduled_date 전달 불필요) | + +> 단, 공정 매칭 미리보기 UI에서 STOCK 수주가 BOM 없이도 "절곡" 공정으로 표시되도록 조건 분기 필요할 수 있음. + +### 3.5 생산지시 목록 — STOCK 건 표시 (기존 페이지 수정) + +**경로**: `/sales/order-management-sales/production-orders/page.tsx` + +STOCK 타입 재고생산의 생산지시도 이 목록에 표시된다. 변경 필요 사항: + +| 컬럼 | 현재 | STOCK 건 표시 | +|------|------|-------------| +| 현장명 | `siteName` | `'재고생산'` (API가 자동 설정하므로 변경 불필요) | +| 거래처 | `clientName` | 빈 값 또는 `-` | +| 납기 | `deliveryDate` | `-` 표시 (STOCK은 delivery_date 없음) | +| 생산지시일 | `productionOrderedAt` | 정상 표시 | + +**구현 방법**: API 응답에 `order_type_code`가 이미 포함됨. 프론트 types에 추가: + +```typescript +// types.ts 수정 +interface ApiProductionOrder { + // ... 기존 필드 + order_type_code?: string; // 추가 +} + +// transform 함수에서 +orderTypeCode: data.order_type_code || 'ORDER', +``` + +테이블 렌더링에서: + +```typescript +// 납기 컬럼 +{item.orderTypeCode === 'STOCK' ? '-' : item.deliveryDate} + +// 거래처 컬럼 +{item.orderTypeCode === 'STOCK' ? '-' : item.clientName} +``` + +--- + +## 4. 메뉴 등록 + +메뉴 위치: + +``` +판매관리 +├── 거래처관리 +├── 견적관리 +├── 수주관리 +├── 재고생산관리 ← 신규 (수주관리 아래) +└── 단가관리 +``` + +> 메뉴 등록은 MNG 메뉴 동기화 기능으로 별도 처리. 프론트에서는 라우트만 구현. + +--- + +## 5. 수주관리와의 차이 요약 + +``` +┌─ 수주관리 ──────────────────┐ ┌─ 재고생산관리 ───────────────┐ +│ │ │ │ +│ 거래처 선택 │ │ 생산 사유 (텍스트) │ +│ 현장명 │ │ 목표 재고 수량 │ +│ 견적 연결 │ │ 비고 │ +│ 배송/납기/운임 │ │ │ +│ 할인/금액 계산 │ ├──────────────────────────────┤ +│ 매출 인식 방식 │ │ 품목 목록 │ +│ │ │ - 품목 선택 │ +│ 개소/구역 트리 (OrderNode) │ │ - 수량만 입력 │ +│ 품목 목록 (상세) │ │ (단가/금액/할인 불필요) │ +│ - 단가, 할인, 금액 계산 │ │ (개소/구역 불필요) │ +│ - floor_code, symbol_code │ │ │ +│ │ └──────────────────────────────┘ +└──────────────────────────────┘ +``` + +--- + +## 6. API 빠른 참조 + +### 6.1 목록 조회 + +``` +GET /api/v1/orders?order_type=STOCK&page=1&size=20 +``` + +### 6.2 통계 + +``` +GET /api/v1/orders/stats?order_type=STOCK +``` + +### 6.3 생성 + +``` +POST /api/v1/orders +Content-Type: application/json + +{ + "order_type_code": "STOCK", + "memo": "...", + "options": { "production_reason": "...", "target_stock_qty": 500 }, + "items": [{ "item_name": "...", "quantity": 100, "unit_price": 0 }] +} +``` + +### 6.4 상태 변경 + +``` +PATCH /api/v1/orders/{id}/status +{ "status": "CONFIRMED" } +``` + +### 6.5 생산지시 생성 + +``` +POST /api/v1/orders/{id}/production-order +{ "priority": "normal" } +``` + +> 공정/예정일은 API가 자동 처리 (절곡 공정, scheduled_date=now()) + +### 6.6 수정/삭제 + +``` +PUT /api/v1/orders/{id} — 수정 +DELETE /api/v1/orders/{id} — 삭제 +``` + +--- + +## 7. 구현 체크리스트 + +- [ ] `stocks/page.tsx` — 재고생산 목록 페이지 +- [ ] `stocks/[id]/page.tsx` — 재고생산 상세 페이지 +- [ ] `StockProductionRegistration.tsx` — 등록/수정 폼 (수주 폼 단순화) +- [ ] Server Actions — `getOrders({ order_type: 'STOCK' })` 등 (기존 actions.ts 활용) +- [ ] 생산지시 목록 — STOCK 건 납기 `-` 표시, `order_type_code` 타입 추가 +- [ ] 메뉴 라우트 등록 + +--- + +## 관련 문서 + +- [API 명세 (상세)](../frontend/api-specs/stock-production-api.md) +- [기능 설명](../features/sales/stock-production.md) +- [개발 변경이력](../dev/changes/20260316_stock_production_order.md) + +--- + +**최종 업데이트**: 2026-03-16 From 077a0a3331a8a9901477467e99c30fadc72d3a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 22:59:43 +0900 Subject: [PATCH 13/19] =?UTF-8?q?docs:=20[sales]=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=EC=83=9D=EC=82=B0=EA=B4=80=EB=A6=AC=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 재공품(WIP) 개념 설명 추가 - Phase 1/2 구현 범위 정리 - 작업자 화면 절곡 탭 향후 과제 기록 - 전체 메뉴 영향범위 검토 결과 추가 --- features/sales/stock-production.md | 57 +++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/features/sales/stock-production.md b/features/sales/stock-production.md index cf8d0d1..8780539 100644 --- a/features/sales/stock-production.md +++ b/features/sales/stock-production.md @@ -94,7 +94,49 @@ orders 테이블 --- -## 5. API +## 5. 재공품(WIP) 개념 + +재고생산의 대상 품목은 **재공품(Work-In-Process)**이다. + +- 수주를 받지 않았지만, 회사 내에서 **이미 정해진 절곡품**을 미리 생산 +- 작업자들의 **유휴시간에 앞으로 나갈 물건을 미리 제작**하는 행위 +- 실제 수주가 들어오면 이미 만들어둔 재공품을 사용하여 **납기 단축** + +``` +유휴시간 → 재공품 미리 생산 → 재고 적재 + ↓ +실제 수주 → 재고에서 재공품 사용 → 납기 단축 +``` + +### 5.1 현재 구현 범위 (Phase 1) + +- ✅ 재고생산 등록/목록/상세/수정/삭제 +- ✅ 생산지시 생성 (절곡 공정 자동 선택) +- ✅ 생산지시 목록에 표시 +- ✅ 작업지시 목록에 표시 +- ✅ 생산현황판에 표시 +- ✅ 작업실적 조회에 표시 + +### 5.2 향후 과제 (Phase 2) + +> **작업자 화면 절곡 탭 — 재공품 사양 정보 연동** + +현재 STOCK 건은 OrderNode(개소)가 없어 BendingInfoBuilder가 동작하지 않는다. +절곡 작업자 화면에서 다음 정보가 누락된다: + +| 누락 정보 | 원인 | 해결 방향 | +|----------|------|----------| +| W/H (가로/세로) | OrderNode.options 없음 | 품목 마스터에 규격 정보 보유 → 등록 시 가져오기 | +| bending_info | BendingInfoBuilder 실패 (rootNodes 없음) | 품목별 표준 BOM 활용 또는 수동 입력 | +| 동적 자재 목록 | dynamic_bom 미생성 | 품목별 표준 BOM에서 자재 목록 생성 | +| 제품코드 | nodeOptions 없음 | 품목 마스터의 item_code 활용 | + +**해결 전략**: 재공품은 이미 정해진 규격품이므로, 품목관리의 품목 정보(규격, BOM)를 +재고생산 등록 시 `work_order_item.options`에 채워넣는 방식으로 해결 가능. + +--- + +## 6. API 기존 수주관리 API를 그대로 사용한다. `order_type=STOCK` 파라미터로 필터링한다. @@ -102,6 +144,19 @@ orders 테이블 --- +## 7. 영향범위 검토 결과 + +| 메뉴 | 수정 필요 | 비고 | +|------|:--------:|------| +| 재고생산관리 (신규) | ✅ React 구현 | 수주 화면 단순화 | +| 생산지시 목록 | ⚠️ 소규모 | 납기/거래처 `-` 표시 (프론트) | +| 생산현황판 | ❌ 없음 | project_name='재고생산' 정상 표시 | +| 작업지시 목록 | ❌ 없음 | null fallback 처리 완비 | +| 작업실적 조회 | ❌ 없음 | Order 정보 미참조 | +| 작업자 화면 (절곡) | ⚠️ Phase 2 | bending_info/W/H 누락 (향후 과제) | + +--- + ## 관련 문서 - [재고생산관리 API 명세](../../frontend/api-specs/stock-production-api.md) From 7d280f3dd56cda4d69364c1d31aed8c85769feb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 16 Mar 2026 23:02:41 +0900 Subject: [PATCH 14/19] =?UTF-8?q?docs:=20[rules]=20=EC=9E=AC=EA=B3=B5?= =?UTF-8?q?=ED=92=88(WIP)=20=EC=83=9D=EC=82=B0=20=EC=A0=95=EC=B1=85=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 재공품 개념 정의 (원자재 → 재공품 → 완성품) - 재공품 미리 생산의 4가지 효과 (유휴시간 활용, 납기 단축, 생산 평준화, 긴급 대응) - MTS/MTO/ATO 제조 전략 설명 - 제조업 공통 적용 사례 (가구, 자동차, 전자제품, 식품, 의류) - 과잉 생산 방지 주의사항 - SAM 시스템 처리 방식 (내부 오더, 재고 생명주기) --- INDEX.md | 2 + rules/wip-production-policy.md | 326 +++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 rules/wip-production-policy.md diff --git a/INDEX.md b/INDEX.md index 6fde46d..0d3930e 100644 --- a/INDEX.md +++ b/INDEX.md @@ -24,6 +24,7 @@ | 견적관리 | `features/quotes/README.md` | 견적 시스템, BOM 계산 | | 급여관리 API | `frontend/api-specs/payroll-api.md` | 급여관리 API 전체 명세 (18개 엔드포인트) | | 바로빌 회계 API | `frontend/api-specs/barobill-api.md` | 카드/은행/홈택스 REST API (42개 엔드포인트) | +| 재공품 생산 정책 | `rules/wip-production-policy.md` | 재공품(WIP) 개념, 제조업 공통 패턴, SAM 처리 방식 | | 재고생산관리 API | `frontend/api-specs/stock-production-api.md` | 재고생산 API 명세 (기존 수주 API + STOCK 타입) | | 결재관리 | `dev/dev_plans/approval-system-unification-plan.md` | MNG→API 결재 통합 계획 | | API 품질 | `system/api-code-quality-audit.md` | 정석 패턴 R1~R6, 안티패턴, 보안, 체크리스트 | @@ -139,6 +140,7 @@ DB 도메인별: | [pricing-policy.md](rules/pricing-policy.md) | 단가 정책 | | [numbering-rules.md](rules/numbering-rules.md) | 채번 규칙 | | [client-policy.md](rules/client-policy.md) | 고객사 관리 정책 | +| [wip-production-policy.md](rules/wip-production-policy.md) | 재공품(WIP) 생산 정책 (개념, MTS/MTO/ATO, 제조업 공통 패턴) | | [billing-policy.md](rules/billing-policy.md) | 과금 정책 (CONFIDENTIAL) | | [customer-pricing.md](rules/customer-pricing.md) | 고객 요금표 | | [partner-commission.md](rules/partner-commission.md) | 영업파트너 수당 체계 | diff --git a/rules/wip-production-policy.md b/rules/wip-production-policy.md new file mode 100644 index 0000000..43a90ab --- /dev/null +++ b/rules/wip-production-policy.md @@ -0,0 +1,326 @@ +# 재공품(WIP) 생산 정책 + +> **작성일**: 2026-03-16 +> **상태**: 설계 확정 + +--- + +## 1. 개요 + +### 1.1 목적 + +제조업에서 **재공품(WIP, Work-In-Process)** 생산이 왜 필요하고, SAM 시스템에서 어떻게 처리하는지 정의한다. + +### 1.2 대상 독자 + +- SAM을 도입하는 모든 제조업체 +- 시스템 운영자 및 생산관리 담당자 +- 개발팀 (기능 설계 시 참고) + +--- + +## 2. 재공품이란 + +### 2.1 정의 + +재공품(WIP)은 **완성품이 되기 전 단계의 중간 생산물**이다. + +블라인드/스크린 제조업에서는 절곡 가공된 **가이드레일, 케이스, 마구리, 바텀바** 등이 대표적인 재공품이다. 이 부품들은 최종 제품(블라인드)에 조립되기 전까지는 재공품 상태로 재고에 보관된다. + +``` +원자재 (알루미늄 코일, 철판 등) + ↓ 절곡 가공 +재공품 (가이드레일, 케이스, 바텀바 등) ← 이 단계 + ↓ 조립 +완성품 (블라인드, 스크린) + ↓ 출하 +고객 납품 +``` + +### 2.2 재공품 vs 완성품 vs 원자재 + +| 구분 | 설명 | 예시 | +|------|------|------| +| **원자재** | 가공 전 소재 | 알루미늄 코일, 철판, SUS판 | +| **재공품 (WIP)** | 가공 완료, 조립 전 | 가이드레일, 케이스, 바텀바, 마구리 | +| **완성품** | 출하 가능 상태 | 완성된 블라인드, 스크린 | + +--- + +## 3. 왜 재공품을 미리 만드는가 + +### 3.1 제조업의 현실 + +모든 제조업에는 **수주가 몰리는 성수기**와 **한가한 비수기**가 존재한다. + +``` +┌────────────────────────────────────────────────┐ +│ 수주량 │ +│ ▲ │ +│ │ ╱╲ ╱╲ │ +│ │ ╱╲ ╱ ╲ ╱ ╲ ╱╲ │ +│ │╱ ╲╱ ╲╱ ╲╱ ╲ │ +│ └──────────────────────────── ▶ 시간 │ +│ 비수기 성수기 비수기 성수기 │ +│ │ +│ ✕ 비수기: 작업자 유휴 → 인건비 낭비 │ +│ ✕ 성수기: 작업 폭주 → 납기 지연 │ +└────────────────────────────────────────────────┘ +``` + +### 3.2 재공품 미리 생산의 효과 + +``` +┌─────────────────────────────────────────────────┐ +│ │ +│ ① 유휴시간 활용 │ +│ 비수기에 작업자들이 놀지 않고 재공품 생산 │ +│ → 인건비 대비 생산성 향상 │ +│ │ +│ ② 납기 단축 │ +│ 수주가 들어오면 이미 만들어둔 재공품 사용 │ +│ → 절곡 가공 시간 절약 → 납기 3~5일 단축 │ +│ │ +│ ③ 생산 평준화 │ +│ 성수기 작업 부하를 비수기로 분산 │ +│ → 잔업/야근 감소 → 품질 안정 │ +│ │ +│ ④ 긴급 수주 대응 │ +│ 재고가 있으면 긴급 주문도 즉시 대응 가능 │ +│ → 고객 만족도 향상 → 수주 확보 │ +│ │ +└─────────────────────────────────────────────────┘ +``` + +### 3.3 어떤 품목을 미리 만드는가 + +모든 재공품을 무작정 만들지는 않는다. **다음 조건에 해당하는 품목**만 미리 생산한다: + +| 조건 | 설명 | 예시 | +|------|------|------| +| **규격 표준화** | 사이즈/재질이 정해져 있음 | 가이드레일 2438mm, 3000mm | +| **수요 빈도 높음** | 자주 출고되는 품목 | 주력 제품의 핵심 부품 | +| **가공 시간 김** | 만들기 시간이 오래 걸림 | 절곡 가공 (절단→벤딩→용접) | +| **보관 용이** | 장기 보관해도 품질 저하 없음 | 금속 부품 (부식 방지 처리 완료) | + +> **주의**: 주문 제작품(고객 맞춤 사이즈)은 재공품 대상이 아니다. 표준 규격품만 해당한다. + +--- + +## 4. SAM 시스템에서의 처리 방식 + +### 4.1 설계 원칙 + +SAM은 **수주 → 생산지시 → 작업지시 → 생산**의 흐름으로 동작한다. 수주가 없으면 생산지시를 생성할 수 없는 구조이다. + +재공품 생산은 수주가 없지만 생산이 필요한 경우이므로, **내부 오더(Internal Order)** 개념을 도입하여 해결한다. + +``` +일반 수주: 고객 주문 → 수주 등록 → 생산지시 → 작업지시 → 생산 +재공품 생산: (주문 없음) → 재고생산 등록 → 생산지시 → 작업지시 → 생산 + ↑ + 내부 오더 (order_type_code = 'STOCK') +``` + +### 4.2 기존 테이블 공유 + +재공품 생산을 위해 별도 테이블을 만들지 않는다. 기존 `orders` 테이블에 `order_type_code = 'STOCK'`을 추가하여 구분한다. + +``` +orders 테이블 +├── ORDER → 고객 수주 (거래처 주문) +├── PURCHASE → 발주 (자재 구매) +└── STOCK → 재고생산 (내부 오더, 재공품) +``` + +**이유**: +- 하류 시스템(작업지시, 생산, 품질검사, 출하)을 **변경 없이** 재활용 +- 생산 통계에 재공품 생산량도 **자동 포함** +- 이력 관리(누가, 언제, 얼마나 재공품을 생산했는지) **기존 구조로 추적** + +### 4.3 재공품 생산 흐름 + +``` +┌─────────────────────────────────────────────────┐ +│ │ +│ 1. 재고생산 등록 │ +│ ├─ 품목 선택 (이미 정해진 재공품) │ +│ ├─ 수량 입력 │ +│ ├─ 생산 사유 입력 (선택) │ +│ └─ order_type_code = 'STOCK' 자동 설정 │ +│ │ +│ 2. 재고생산 확정 │ +│ ├─ 상태: DRAFT → CONFIRMED │ +│ └─ ※ 매출 생성 안 함 (내부 오더이므로) │ +│ │ +│ 3. 생산지시 생성 │ +│ ├─ 절곡 공정 자동 선택 │ +│ ├─ 현장명 = '재고생산' 자동 설정 │ +│ ├─ 작업예정일 = 오늘 날짜 │ +│ └─ 작업지시(WorkOrder) 생성 │ +│ │ +│ 4. 절곡 작업 수행 │ +│ ├─ 작업자가 절곡 공정 수행 │ +│ ├─ 자재 투입 기록 │ +│ ├─ 중간검사 │ +│ └─ 작업 완료 처리 │ +│ │ +│ 5. 재고 입고 │ +│ ├─ 완성된 재공품 → 재고(Stock Lot) 등록 │ +│ └─ 향후 수주 시 재고에서 출고 │ +│ │ +└─────────────────────────────────────────────────┘ +``` + +### 4.4 일반 수주와의 차이 + +| 항목 | 일반 수주 | 재공품 생산 | +|------|----------|-----------| +| 트리거 | 고객 주문 | 생산 계획 (유휴시간 활용) | +| 거래처 | 있음 | 없음 (내부 오더) | +| 견적 | 있음 (선택) | 없음 | +| 납기 | 고객 요청일 | 없음 (가능한 빨리) | +| 공정 선택 | BOM 기반 자동 매칭 | 절곡 공정 고정 | +| 금액/매출 | 계산 + 매출 인식 | 없음 | +| 현장명 | 고객 현장 | '재고생산' 고정 | +| 채번 | `ORD20260316xxxx` | `STK20260316xxxx` | +| 생산 결과 | 고객 출하 | 재고 입고 | + +--- + +## 5. 재공품 재고 관리 + +### 5.1 재공품 재고의 생명주기 + +``` +재공품 생산 완료 + ↓ +재고 입고 (Stock Lot 생성) + ↓ +재고 보관 (창고/적재대) + ↓ +수주 접수 → 재고 확인 + ↓ +재고에서 출고 → 조립 → 완성품 + ↓ +고객 출하 +``` + +### 5.2 재고 확인 시점 + +수주가 들어오면 시스템에서 **해당 재공품이 재고에 있는지 자동 확인**한다: + +- **재고 있음** → 절곡 공정 스킵, 바로 조립 단계로 이동 → 납기 단축 +- **재고 없음** → 일반 생산 흐름 (절곡 공정부터 시작) + +### 5.3 안전재고와 목표 수량 + +재고생산 등록 시 `목표 재고 수량(target_stock_qty)`을 설정할 수 있다. + +| 항목 | 설명 | +|------|------| +| 목표 재고 수량 | 이 수량만큼 재고를 유지하고 싶은 목표 | +| 현재 재고 수량 | Stock Lot에서 조회 | +| 생산 필요 수량 | 목표 - 현재 = 부족분 | + +--- + +## 6. 제조업 공통 패턴 + +### 6.1 Make-to-Stock (MTS) vs Make-to-Order (MTO) + +SAM 시스템은 기본적으로 **MTO(주문생산)** 방식이다. 재공품 생산은 **MTS(재고생산)** 방식을 부분적으로 도입한 것이다. + +| 전략 | 설명 | SAM 적용 | +|------|------|----------| +| **MTO** (주문생산) | 수주 → 생산 | 일반 수주 (`ORDER`) | +| **MTS** (재고생산) | 예측 → 생산 → 재고 | 재고생산 (`STOCK`) | +| **ATO** (주문조립) | 재공품 재고 + 수주 → 조립 | MTS + MTO 결합 | + +블라인드/스크린 제조에서는 **ATO 패턴**이 가장 효율적이다: + +``` +MTS: 표준 재공품 미리 생산 (절곡) + ↓ +ATO: 수주 → 재공품 + 맞춤 부품 → 조립 → 출하 + ↓ + 납기 단축 + 유휴시간 활용 +``` + +### 6.2 다른 제조업 적용 사례 + +재공품 생산은 블라인드/스크린에 국한되지 않는다. **모든 제조업**에서 동일한 패턴이 적용된다: + +| 업종 | 원자재 | 재공품 (WIP) | 완성품 | +|------|--------|-------------|--------| +| **블라인드/스크린** | 알루미늄 코일, 철판 | 가이드레일, 케이스, 바텀바 | 블라인드, 스크린 | +| **가구** | 목재, MDF | 재단된 판재, 엣지밴딩 부품 | 책상, 의자, 캐비닛 | +| **자동차** | 철강, 플라스틱 | 프레스 부품, 엔진 블록 | 자동차 | +| **전자제품** | PCB 원판, 칩 | 조립된 PCB, 하우징 | 스마트폰, TV | +| **식품** | 밀가루, 설탕 | 반죽, 소스 | 빵, 과자 | +| **의류** | 원단, 실 | 재단 원단, 염색 원단 | 완성 의류 | + +### 6.3 재공품 생산의 핵심 가치 + +``` +┌─────────────────────────────────────────────────┐ +│ │ +│ 💰 비용 절감 │ +│ 유휴 인건비를 생산으로 전환 │ +│ → 작업자가 놀지 않고 가치를 창출 │ +│ │ +│ ⏱️ 납기 단축 │ +│ 수주 → 즉시 조립 가능 (절곡 공정 스킵) │ +│ → 고객 만족도 향상 │ +│ │ +│ 📊 생산 평준화 │ +│ 성수기/비수기 작업량 균등화 │ +│ → 잔업 감소, 품질 안정 │ +│ │ +│ 🚀 경쟁력 확보 │ +│ 빠른 납기 = 수주 확보 │ +│ → 매출 증대 │ +│ │ +└─────────────────────────────────────────────────┘ +``` + +--- + +## 7. 주의사항 + +### 7.1 과잉 생산 방지 + +재공품을 너무 많이 만들면 **재고 비용 증가**와 **사장 재고(불용 재고)** 문제가 발생한다. + +| 위험 | 설명 | 대응 | +|------|------|------| +| 과잉 재고 | 수요보다 많이 생산 | 목표 수량 설정, 주기적 재고 점검 | +| 사장 재고 | 규격 변경/단종으로 사용 불가 | 표준 규격품만 대상, 수요 예측 기반 | +| 보관 비용 | 창고 공간 점유 | 적정 재고 수준 유지 | +| 품질 저하 | 장기 보관 시 녹/변색 | 보관 환경 관리, 선입선출(FIFO) | + +### 7.2 생산 대상 품목 선정 기준 + +``` +❌ 주문 맞춤 제작품 (고객 사이즈) +❌ 수요 불확실 품목 +❌ 원자재 가격 변동이 큰 품목 +❌ 보관이 어려운 품목 + +✅ 표준 규격품 (정해진 사이즈) +✅ 출고 빈도 높은 품목 +✅ 가공 시간이 긴 품목 +✅ 장기 보관 가능 품목 +``` + +--- + +## 관련 문서 + +- [재고생산관리 기능 설명](../features/sales/stock-production.md) +- [재고생산관리 API 명세](../frontend/api-specs/stock-production-api.md) +- [품목 정책](item-policy.md) + +--- + +**최종 업데이트**: 2026-03-16 From 469efce44018255d0c047a8c3039e12db4f9f7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 17 Mar 2026 08:54:02 +0900 Subject: [PATCH 15/19] =?UTF-8?q?docs:=20[barobill]=20=EB=B0=94=EB=A1=9C?= =?UTF-8?q?=EB=B9=8C=20=EC=97=B0=EB=8F=99=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SOAP API 구조, 테스트/운영 모드 비교 - 과금 정책 및 멀티테넌트 처리 - 서비스 이관 계획 및 우선순위 - INDEX.md에 문서 등록 --- INDEX.md | 2 + features/barobill/README.md | 397 ++++++++++++++++++++++++++++++++++++ 2 files changed, 399 insertions(+) create mode 100644 features/barobill/README.md diff --git a/INDEX.md b/INDEX.md index 0d3930e..d850a45 100644 --- a/INDEX.md +++ b/INDEX.md @@ -24,6 +24,7 @@ | 견적관리 | `features/quotes/README.md` | 견적 시스템, BOM 계산 | | 급여관리 API | `frontend/api-specs/payroll-api.md` | 급여관리 API 전체 명세 (18개 엔드포인트) | | 바로빌 회계 API | `frontend/api-specs/barobill-api.md` | 카드/은행/홈택스 REST API (42개 엔드포인트) | +| 바로빌 시스템 | `features/barobill/README.md` | SOAP 연동, 테스트/운영 모드, 과금, 멀티테넌트, 이관 계획 | | 재공품 생산 정책 | `rules/wip-production-policy.md` | 재공품(WIP) 개념, 제조업 공통 패턴, SAM 처리 방식 | | 재고생산관리 API | `frontend/api-specs/stock-production-api.md` | 재고생산 API 명세 (기존 수주 API + STOCK 타입) | | 결재관리 | `dev/dev_plans/approval-system-unification-plan.md` | MNG→API 결재 통합 계획 | @@ -170,6 +171,7 @@ DB 도메인별: | [sales/stock-production.md](features/sales/stock-production.md) | 재고생산관리 (내부 오더 방식, 수주 테이블 공유) | | [sales/demo-tenant-policy.md](features/sales/demo-tenant-policy.md) | 영업파트너 데모 테넌트 정책 (3-Tier 전략) | | [sales/demo-tenant-usage-guide.md](features/sales/demo-tenant-usage-guide.md) | 데모 테넌트 사용 가이드 (영업파트너/관리자용) | +| [barobill/README.md](features/barobill/README.md) | 바로빌 연동 시스템 (SOAP API, 테스트/운영 모드, 과금, 이관 계획) | | [barobill-kakaotalk/README.md](features/barobill-kakaotalk/README.md) | 바로빌 카카오톡 | | [quality-management/README.md](features/quality-management/README.md) | 품질관리 (제품검사, 실적신고) | | [approvals/README.md](features/approvals/README.md) | 결재관리 시스템 | diff --git a/features/barobill/README.md b/features/barobill/README.md new file mode 100644 index 0000000..cf81581 --- /dev/null +++ b/features/barobill/README.md @@ -0,0 +1,397 @@ +# 바로빌(Barobill) 연동 시스템 + +> **작성일**: 2026-03-17 +> **상태**: MNG 운영 중 / 서비스 이관 준비 + +--- + +## 1. 개요 + +### 1.1 목적 + +바로빌은 전자세금계산서, 계좌조회, 카드내역, 홈택스 연동, 카카오톡/SMS 발송 등을 제공하는 SOAP 기반 B2B 서비스다. SAM에서 재무/회계 데이터를 자동 수집하고 세금계산서를 발행하기 위해 연동한다. + +### 1.2 현재 상태 + +| 항목 | MNG (백오피스) | API (서비스) | React (프론트) | +|------|:------------:|:----------:|:------------:| +| SOAP 연동 서비스 | ✅ 완료 (1,761줄) | 기본 설정만 | — | +| 회원사 관리 | ✅ 운영 중 | 모델만 존재 | 설정 페이지 | +| 카드 거래 조회 | ✅ 운영 중 | ✅ REST API 16개 | — | +| 은행 거래 조회 | ✅ 운영 중 | ✅ REST API 13개 | — | +| 홈택스 세금계산서 | ✅ 운영 중 | ✅ REST API 13개 | — | +| 카카오톡/SMS | ✅ 운영 중 | — | — | +| 과금 시스템 | ✅ 구현 완료 | — | — | + +> **핵심**: tenant_id=1 (코드브릿지엑스 본사)에서 실무 운영 중. 서비스 이관 시 멀티테넌트 SOAP 연동이 핵심 과제. + +### 1.3 바로빌 공식 자료 + +- 개발자 센터: `https://dev.barobill.co.kr/` +- 운영 WSDL: `https://ws.baroservice.com/` +- 테스트 WSDL: `https://testws.baroservice.com/` + +--- + +## 2. 테스트 모드 vs 운영 모드 + +> **경고: 개발 시 반드시 테스트 모드를 사용한다. 운영 모드는 실제 과금이 발생한다.** + +### 2.1 모드 비교 + +| 항목 | 테스트 모드 | 운영 모드 | +|------|-----------|----------| +| WSDL 엔드포인트 | `https://testws.baroservice.com/` | `https://ws.baroservice.com/` | +| CERTKEY | 테스트용 별도 발급 | 운영용 별도 발급 | +| 과금 | ❌ 무과금 | ✅ 실제 과금 | +| 데이터 | 테스트 데이터 (초기화 가능) | 실제 세금계산서/거래 데이터 | +| 국세청 전송 | ❌ 미전송 | ✅ 실제 전송 | +| 회원사 등록 | 테스트 서버에 등록 | 운영 서버에 등록 | +| 인증서 | 테스트 인증서 사용 가능 | 실제 공동인증서 필수 | + +### 2.2 모드 전환 구조 + +``` +┌───────────────────────────────────────────────────────┐ +│ 모드 결정 흐름 │ +├───────────────────────────────────────────────────────┤ +│ │ +│ 1. BarobillMember.server_mode │ +│ └─ 'test' 또는 'production' (회원사별 설정) │ +│ │ +│ 2. BarobillService.switchServerMode(isTestMode) │ +│ └─ SOAP 클라이언트 재초기화 │ +│ └─ initializeConfig() 호출 │ +│ │ +│ 3. initializeConfig() │ +│ ├─ DB 우선: BarobillConfig.getActive(isTestMode) │ +│ │ └─ environment = 'test' | 'production' │ +│ └─ .env 폴백: │ +│ ├─ test → BAROBILL_CERT_KEY_TEST │ +│ └─ prod → BAROBILL_CERT_KEY_PROD │ +│ │ +│ 4. SOAP URL 구성 │ +│ ├─ test → testws.baroservice.com/* │ +│ └─ prod → ws.baroservice.com/* │ +│ │ +└───────────────────────────────────────────────────────┘ +``` + +### 2.3 설정 우선순위 + +1. **DB 설정** (`barobill_configs` 테이블) — 최우선 +2. **.env 환경변수** — DB 설정 없을 때 폴백 + +```php +// BarobillService::initializeConfig() +$dbConfig = BarobillConfig::getActive($this->isTestMode); + +if ($dbConfig) { + // DB에서 cert_key, corp_num, base_url 사용 +} else { + // .env에서 BAROBILL_CERT_KEY_TEST/PROD, BAROBILL_CORP_NUM 사용 +} +``` + +### 2.4 환경변수 + +```bash +# .env (MNG, API 동일) +BAROBILL_CERT_KEY_TEST=<테스트 인증키> +BAROBILL_CERT_KEY_PROD=<운영 인증키> +BAROBILL_CORP_NUM=<파트너 사업자번호> +BAROBILL_TEST_MODE=true # 기본값: 테스트 모드 +``` + +### 2.5 개발 시 주의사항 + +``` +✅ 로컬/개발 서버: BAROBILL_TEST_MODE=true (기본값) +✅ 운영 서버: BAROBILL_TEST_MODE=false + 운영 CERTKEY +✅ 회원사별 server_mode로 개별 전환 가능 +❌ 테스트 CERTKEY로 운영 서버 호출 불가 (에러 -11102) +❌ 운영 모드에서 테스트 데이터 생성 금지 (실제 과금) +``` + +--- + +## 3. 아키텍처 + +### 3.1 전체 데이터 흐름 + +``` +바로빌 SOAP API (ws.baroservice.com) + │ + │ SOAP (6개 서비스) + ▼ +┌─────────────────────────────────┐ +│ MNG (BarobillService) │ +│ ├─ CORPSTATE — 회원사 관리 │ +│ ├─ TI — 전자세금계산서 │ +│ ├─ BANKACCOUNT — 계좌조회 │ +│ ├─ CARD — 카드조회 │ +│ ├─ KAKAOTALK — 알림톡 │ +│ └─ SMS — 문자 발송 │ +└──────────┬──────────────────────┘ + │ MySQL 저장 + ▼ +┌─────────────────────────────────┐ +│ MySQL (samdb) │ +│ ├─ barobill_members │ +│ ├─ barobill_card_transactions │ +│ ├─ barobill_bank_transactions │ +│ ├─ hometax_invoices │ +│ └─ (18개 테이블) │ +└──────────┬──────────────────────┘ + │ REST API + ▼ +┌─────────────────────────────────┐ +│ API (42개 엔드포인트) │ +│ ├─ /api/v1/barobill-card-* │ +│ ├─ /api/v1/barobill-bank-* │ +│ └─ /api/v1/hometax-invoices/* │ +└──────────┬──────────────────────┘ + │ + ▼ +┌─────────────────────────────────┐ +│ React (사용자 UI) │ +│ └─ BarobillIntegration 컴포넌트│ +└─────────────────────────────────┘ +``` + +### 3.2 SOAP 서비스 목록 + +| 서비스 | WSDL 경로 | 기능 | +|--------|----------|------| +| CORPSTATE | `/CORPSTATE.asmx` | 회원사 등록/조회/수정 | +| TI | `/TI.asmx` | 전자세금계산서 발행/조회 | +| BANKACCOUNT | `/BANKACCOUNT.asmx` | 계좌 등록/입출금 내역 조회 | +| CARD | `/CARD.asmx` | 카드 등록/사용내역 조회 | +| KAKAOTALK | `/KAKAOTALK.asmx` | 카카오톡 알림톡 발송 | +| SMS | `/SMS.asmx` | 문자 메시지 발송 | + +### 3.3 인증 구조 + +``` +모든 API 호출 + └─ CERTKEY (파트너 인증키) — 필수 파라미터 + ├─ 바로빌 파트너 계약 시 발급 + ├─ 테스트/운영 별도 키 + └─ BarobillService.call()에서 자동 주입 +``` + +--- + +## 4. 과금 정책 + +### 4.1 바로빌 과금 구조 (SAM 내부 정책) + +| 서비스 | 월정액 | 비고 | +|--------|-------|------| +| 계좌조회 (`bank_account`) | 10,000원/월 | 테넌트별 | +| 카드내역 (`card`) | 10,000원/월 | 테넌트별 | +| 홈택스 매입/매출 (`hometax`) | 0원 | 본사 부담 (무료 제공) | + +### 4.2 추가 과금 (건별) + +`BarobillPricingPolicy` 모델 기반: + +| 서비스 | 무료 기본량 | 추가 과금 단위 | 추가 금액 | +|--------|-----------|-------------|----------| +| 법인카드 등록 (`card`) | 정책 설정값 | 정책 설정값 | 정책 설정값 | +| 계산서 발행 (`tax_invoice`) | 정책 설정값 | 건당 | 정책 설정값 | +| 계좌조회 수집 (`bank_account`) | 정책 설정값 | 정책 설정값 | 정책 설정값 | + +> 과금 계산: `BarobillPricingPolicy::calculateBilling(usageCount)` — 무료 제공량 초과분만 과금 + +### 4.3 과금 처리 흐름 + +``` +매월 1일 (배치) + └─ BarobillBillingService::processMonthlyBilling() + ├─ 활성 구독 조회 (BarobillSubscription::active()) + ├─ 이미 과금된 기록 중복 방지 + ├─ BarobillBillingRecord 생성 (subscription 타입) + └─ BarobillMonthlySummary 갱신 + +건별 발생 시 + └─ BarobillBillingService::recordUsage() + ├─ BarobillBillingRecord 생성 (usage 타입) + └─ BarobillMonthlySummary 갱신 +``` + +### 4.4 테스트 모드에서의 과금 + +``` +✅ 테스트 모드: 바로빌 API 호출에 대한 바로빌 측 과금 없음 +✅ SAM 내부 과금 시스템은 모드와 무관하게 기록 가능 (테스트용) +❌ 운영 모드: 바로빌 측 실제 과금 발생 (충전잔액 차감) +``` + +--- + +## 5. 멀티테넌트 처리 + +### 5.1 데이터 격리 + +모든 바로빌 테이블은 `tenant_id` 컬럼으로 데이터를 격리한다. + +``` +tenant_id=1 (코드브릿지엑스) → 본사 실무 데이터 +tenant_id=N (고객사) → 해당 고객사 데이터만 접근 +``` + +### 5.2 회원사별 설정 + +각 테넌트는 `barobill_members` 테이블에 독립된 바로빌 회원사 정보를 가진다: + +| 컬럼 | 설명 | +|------|------| +| `tenant_id` | 테넌트 FK | +| `biz_no` | 사업자번호 (UNIQUE with tenant_id) | +| `barobill_id` | 바로빌 로그인 ID | +| `barobill_pwd` | 바로빌 비밀번호 (Laravel Encryption) | +| `server_mode` | `test` 또는 `production` (회원사별 전환) | +| `status` | `active` / `inactive` / `pending` | + +### 5.3 서비스 이관 시 고려사항 + +``` +🔴 필수: 테넌트별 CERTKEY 관리 방안 (현재는 전역 1개) +🔴 필수: 테넌트 온보딩 시 바로빌 회원 자동 등록 플로우 +🟡 중요: 테스트→운영 모드 전환 프로세스 정의 +🟡 중요: 과금 정책을 테넌트별로 다르게 적용 가능하도록 확장 +🟢 권장: 바로빌 API 호출 로그/모니터링 +``` + +--- + +## 6. 프로젝트별 코드 위치 + +### 6.1 MNG (`/home/aweso/sam/mng`) + +| 유형 | 경로 | +|------|------| +| 서비스 | `app/Services/Barobill/BarobillService.php` (1,761줄, 핵심) | +| 서비스 | `app/Services/Barobill/HometaxSyncService.php` | +| 서비스 | `app/Services/Barobill/BarobillBillingService.php` | +| 서비스 | `app/Services/Barobill/BarobillUsageService.php` | +| 서비스 | `app/Services/Barobill/BarobillBankSyncService.php` | +| 모델 | `app/Models/Barobill/` (18개 모델) | +| 컨트롤러 | `app/Http/Controllers/Barobill/` (7개) | +| Admin API | `app/Http/Controllers/Api/Admin/Barobill/` (7개) | +| 뷰 | `resources/views/barobill/` (10개 페이지) | + +### 6.2 API (`/home/aweso/sam/api`) + +| 유형 | 경로 | +|------|------| +| 서비스 | `app/Services/BarobillService.php` (기본 설정만) | +| 모델 | `app/Models/Barobill/` (15개) | +| 모델 | `app/Models/Tenants/BarobillSetting.php` | +| 컨트롤러 | `app/Http/Controllers/Api/V1/Barobill*Controller.php` | +| 마이그레이션 | `database/migrations/` (19개 바로빌 관련) | + +### 6.3 React (`/home/aweso/sam/react`) + +| 유형 | 경로 | +|------|------| +| 컴포넌트 | `src/components/settings/BarobillIntegration/` | +| 페이지 | `/settings/barobill-integration` | + +--- + +## 7. DB 테이블 구조 + +### 7.1 테이블 목록 + +| 테이블 | 용도 | 마이그레이션 위치 | +|--------|------|-----------------| +| `barobill_members` | 회원사 정보 | API | +| `barobill_configs` | API 설정 (test/prod 분리) | API | +| `barobill_settings` | 테넌트별 서비스 설정 | API | +| `barobill_subscriptions` | 월정액 구독 | API | +| `barobill_billing_records` | 과금 기록 | API | +| `barobill_monthly_summaries` | 월별 과금 요약 | API | +| `barobill_pricing_policies` | 요금 정책 | API | +| `hometax_invoices` | 홈택스 세금계산서 | API | +| `hometax_invoice_journals` | 세금계산서 분개 | API | +| `barobill_bank_transactions` | 은행 거래 내역 | API | +| `barobill_bank_transaction_overrides` | 은행 적요 수정 | API | +| `barobill_bank_transaction_splits` | 은행 거래 분할 | API | +| `barobill_bank_sync_status` | 은행 동기화 상태 | API | +| `barobill_card_transactions` | 카드 거래 내역 | API | +| `barobill_card_transaction_splits` | 카드 거래 분할 | API | +| `barobill_card_transaction_amount_logs` | 카드 금액 수정 로그 | API | +| `barobill_card_transaction_hides` | 카드 거래 숨김 | API | +| `account_codes` | 계정과목 마스터 | API | + +### 7.2 핵심 테이블 스키마 + +**barobill_members**: +- `tenant_id` + `biz_no` UNIQUE +- `server_mode`: `test` | `production` +- `barobill_pwd`: Laravel Encryption (복호화 가능, API 호출 시 필요) + +**hometax_invoices**: +- `tenant_id` + `nts_confirm_num` + `invoice_type` UNIQUE +- `invoice_type`: `sales` | `purchase` +- `tax_type`: 1=과세, 2=영세, 3=면세 +- `issue_type`: 1=정발행, 2=역발행 + +--- + +## 8. 에러 코드 매핑 + +| 코드 | 의미 | 대응 | +|------|------|------| +| -11101 | 사업자번호 미설정/유효하지 않음 | 회원사 정보 확인 | +| -11102 | CERTKEY 유효하지 않음 | 테스트/운영 키 확인 | +| -11103 | 인증서 만료/유효하지 않음 | 공동인증서 갱신 | +| -11104 | 미등록 사업자 | 회원사 등록 먼저 | +| -11105 | 이미 등록된 사업자 | 중복 등록 방지 | +| -26001 | 공동인증서 미등록 | 인증서 등록 안내 | +| -32001 | 사업자번호 형식 오류 | 10자리 숫자 확인 | +| -32010 | 이미 등록된 사업자번호 | 기존 회원 확인 | +| -32011 | 이미 등록된 아이디 | 다른 아이디 사용 | + +--- + +## 9. 서비스 이관 계획 + +### 9.1 이관 범위 + +| 기능 | MNG (현재) | API 이관 | React 이관 | 우선순위 | +|------|:---------:|:-------:|:---------:|:-------:| +| SOAP 연동 서비스 | ✅ | 🔴 필수 | — | P1 | +| 회원사 등록/관리 | ✅ | 🔴 필수 | 🔴 필수 | P1 | +| 테스트/운영 모드 전환 | ✅ | 🔴 필수 | 🟡 관리자 | P1 | +| 카드 거래 동기화 | ✅ | 🟡 중요 | ✅ 완료 | P2 | +| 은행 거래 동기화 | ✅ | 🟡 중요 | — | P2 | +| 홈택스 동기화 | ✅ | 🟡 중요 | — | P2 | +| 세금계산서 발행 | ✅ | 🟡 중요 | 🟡 중요 | P2 | +| 과금 시스템 | ✅ | 🟢 권장 | 🟢 권장 | P3 | +| 카카오톡/SMS | ✅ | 🟢 권장 | — | P3 | + +### 9.2 이관 시 핵심 과제 + +1. **SOAP 서비스 이관**: MNG의 `BarobillService` (1,761줄)를 API로 이동 +2. **멀티테넌트 CERTKEY**: 테넌트별로 바로빌 파트너 계약이 필요한지, 공용 CERTKEY로 처리 가능한지 확인 +3. **테스트 모드 관리**: 신규 테넌트는 테스트 모드로 시작 → 관리자가 운영 모드로 전환 +4. **동기화 스케줄러**: MNG에서 실행 중인 은행/카드/홈택스 동기화를 API Queue로 이관 +5. **인증서 관리**: 공동인증서 등록 URL을 테넌트 사용자에게 제공하는 플로우 + +--- + +## 관련 문서 + +| 문서 | 설명 | +|------|------| +| [바로빌 API 명세](../../frontend/api-specs/barobill-api.md) | 카드/은행/홈택스 REST API 42개 엔드포인트 | +| [바로빌 회원 마이그레이션](../../dev/guides/barobill-members-migration.md) | 회원 데이터 이관 가이드 | +| [바로빌 카카오톡](../barobill-kakaotalk/README.md) | 카카오톡 알림톡 연동 | +| [재무관리](../finance/README.md) | 재무/자금관리 전체 개요 | + +--- + +**최종 업데이트**: 2026-03-17 From 79ca132d1abca68df78ceb1fca54b805e595fcb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 17 Mar 2026 09:00:00 +0900 Subject: [PATCH 16/19] =?UTF-8?q?docs:=20[barobill]=20=ED=85=8C=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=98=A8=EB=B3=B4=EB=94=A9=20=EA=B0=9C=EB=85=90=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=20=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 온보딩 vs 베타테스트 개념 구분 - 6단계 온보딩 프로세스 정의 - 테스트→운영 모드 전환 체크리스트 - 개발→베타→출시→온보딩 단계별 관계 정리 --- INDEX.md | 1 + features/barobill/README.md | 1 + features/barobill/tenant-onboarding.md | 190 +++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 features/barobill/tenant-onboarding.md diff --git a/INDEX.md b/INDEX.md index d850a45..4be0827 100644 --- a/INDEX.md +++ b/INDEX.md @@ -172,6 +172,7 @@ DB 도메인별: | [sales/demo-tenant-policy.md](features/sales/demo-tenant-policy.md) | 영업파트너 데모 테넌트 정책 (3-Tier 전략) | | [sales/demo-tenant-usage-guide.md](features/sales/demo-tenant-usage-guide.md) | 데모 테넌트 사용 가이드 (영업파트너/관리자용) | | [barobill/README.md](features/barobill/README.md) | 바로빌 연동 시스템 (SOAP API, 테스트/운영 모드, 과금, 이관 계획) | +| [barobill/tenant-onboarding.md](features/barobill/tenant-onboarding.md) | 바로빌 테넌트 온보딩 (개념 정의, 6단계 프로세스, 베타테스트와의 차이) | | [barobill-kakaotalk/README.md](features/barobill-kakaotalk/README.md) | 바로빌 카카오톡 | | [quality-management/README.md](features/quality-management/README.md) | 품질관리 (제품검사, 실적신고) | | [approvals/README.md](features/approvals/README.md) | 결재관리 시스템 | diff --git a/features/barobill/README.md b/features/barobill/README.md index cf81581..075756a 100644 --- a/features/barobill/README.md +++ b/features/barobill/README.md @@ -387,6 +387,7 @@ tenant_id=N (고객사) → 해당 고객사 데이터만 접근 | 문서 | 설명 | |------|------| +| [테넌트 온보딩](./tenant-onboarding.md) | 온보딩 개념 정의, 테스트→운영 전환 프로세스 | | [바로빌 API 명세](../../frontend/api-specs/barobill-api.md) | 카드/은행/홈택스 REST API 42개 엔드포인트 | | [바로빌 회원 마이그레이션](../../dev/guides/barobill-members-migration.md) | 회원 데이터 이관 가이드 | | [바로빌 카카오톡](../barobill-kakaotalk/README.md) | 카카오톡 알림톡 연동 | diff --git a/features/barobill/tenant-onboarding.md b/features/barobill/tenant-onboarding.md new file mode 100644 index 0000000..859a67a --- /dev/null +++ b/features/barobill/tenant-onboarding.md @@ -0,0 +1,190 @@ +# 바로빌 테넌트 온보딩 프로세스 + +> **작성일**: 2026-03-17 +> **상태**: 설계 중 (서비스 이관 준비) + +--- + +## 1. 온보딩(Onboarding)이란 + +### 1.1 정의 + +**온보딩**: 새로운 고객(테넌트)이 서비스에 가입하여 실제 사용을 시작하기까지의 초기 설정 과정. + +SAM 바로빌 맥락에서는 **정식 계약 고객이 바로빌 연동 기능(계좌조회, 카드내역, 세금계산서 등)을 실무에서 사용할 수 있도록 초기 세팅하는 일련의 절차**를 의미한다. + +### 1.2 온보딩 vs 베타테스트 + +| 구분 | 온보딩 | 베타테스트 | +|------|--------|----------| +| **대상** | 정식 계약 고객 | 서비스 출시 전 검증 참여자 | +| **목적** | 고객이 기능을 쓸 수 있게 초기 세팅 | 서비스 안정성/기능 검증 | +| **시점** | 고객 가입할 때마다 반복 발생 | 서비스 출시 전 1회성 | +| **테스트 모드** | 초기 세팅 확인용으로 잠깐 사용 가능 | 전체 기간 테스트 모드로 운영 | +| **데이터** | 실제 업무 데이터 | 검증용 테스트 데이터 | +| **과금** | 정식 과금 (운영 모드 전환 후) | 무과금 | + +### 1.3 단계별 관계 + +서비스 이관 완료 후 다음 순서로 진행한다: + +``` +서비스 이관 (개발) + └─ 베타테스트 (출시 전 검증) + └─ 정식 출시 + └─ 테넌트 온보딩 (고객 가입 시마다 반복) +``` + +--- + +## 2. 바로빌 온보딩 전체 흐름 + +### 2.1 단계별 프로세스 + +``` +Phase 1: 테넌트 계약 +┌──────────────────────────────────────────┐ +│ 고객사 SAM 서비스 계약 체결 │ +│ └─ SAM 테넌트 생성 (tenant_id 발급) │ +│ └─ 바로빌 서비스 이용 여부 확인 │ +└──────────────────────┬───────────────────┘ + ▼ +Phase 2: 바로빌 회원 등록 (테스트 모드) +┌──────────────────────────────────────────┐ +│ 바로빌 회원사 등록 (BarobillService) │ +│ ├─ 사업자번호, 상호, 대표자 등록 │ +│ ├─ 바로빌 ID/PW 생성 │ +│ ├─ server_mode = 'test' (기본값) │ +│ └─ 테스트 서버에서 연동 확인 │ +└──────────────────────┬───────────────────┘ + ▼ +Phase 3: 인증서 및 계좌/카드 연결 +┌──────────────────────────────────────────┐ +│ 공동인증서 등록 │ +│ ├─ getCertificateRegistUrl() → 고객 직접│ +│ ├─ 인증서 유효성 확인 │ +│ └─ 인증서 만료일 모니터링 설정 │ +│ │ +│ 계좌 연결 │ +│ ├─ getBankAccountScrapRequestUrl() │ +│ └─ 고객이 직접 계좌 등록 │ +│ │ +│ 카드 연결 │ +│ ├─ registCard() │ +│ └─ 카드사별 등록 │ +└──────────────────────┬───────────────────┘ + ▼ +Phase 4: 연동 검증 +┌──────────────────────────────────────────┐ +│ 테스트 모드에서 기능 확인 │ +│ ├─ 계좌 입출금 내역 조회 확인 │ +│ ├─ 카드 사용내역 조회 확인 │ +│ ├─ 홈택스 세금계산서 수집 확인 │ +│ └─ 문제 없으면 다음 단계 │ +└──────────────────────┬───────────────────┘ + ▼ +Phase 5: 운영 모드 전환 +┌──────────────────────────────────────────┐ +│ 관리자가 server_mode → 'production' 전환 │ +│ ├─ 운영 CERTKEY로 SOAP 재연결 │ +│ ├─ 실제 데이터 수집 시작 │ +│ └─ 과금 시작 (구독 등록) │ +└──────────────────────┬───────────────────┘ + ▼ +Phase 6: 실무 사용 시작 +┌──────────────────────────────────────────┐ +│ 정기 동기화 스케줄러 활성화 │ +│ ├─ 은행 거래 자동 수집 │ +│ ├─ 카드 내역 자동 수집 │ +│ ├─ 홈택스 세금계산서 자동 수집 │ +│ └─ 월정액 과금 자동 처리 │ +└──────────────────────────────────────────┘ +``` + +### 2.2 역할 분담 + +| 단계 | 수행 주체 | 설명 | +|------|----------|------| +| 테넌트 생성 | SAM 관리자 | MNG에서 테넌트 생성 | +| 회원사 등록 | SAM 관리자 또는 고객 | 사업자 정보 입력 | +| 인증서 등록 | **고객 직접** | 바로빌 제공 URL에서 직접 등록 | +| 계좌/카드 등록 | **고객 직접** | 바로빌 제공 URL에서 직접 등록 | +| 연동 검증 | SAM 관리자 | 테스트 모드에서 데이터 수집 확인 | +| 운영 전환 | SAM 관리자 | `server_mode` 변경 | + +--- + +## 3. 테스트 모드 활용 + +### 3.1 온보딩에서의 테스트 모드 역할 + +테스트 모드는 **온보딩 Phase 2~4에서 연동을 검증**하기 위해 사용한다. + +``` +✅ 바로빌 회원 등록이 정상적으로 되는지 확인 +✅ 인증서/계좌/카드 연결이 작동하는지 확인 +✅ API 호출이 정상 응답하는지 확인 +❌ 실제 세금계산서 발행 (국세청 미전송) +❌ 실제 거래 데이터 수집 (테스트 데이터만) +``` + +### 3.2 테스트 모드 체크리스트 + +Phase 4 (연동 검증) 완료 기준: + +- [ ] 바로빌 회원 상태: `active` +- [ ] 공동인증서: 등록됨 + 유효기간 확인 +- [ ] 계좌: 1개 이상 등록, 입출금 조회 응답 정상 +- [ ] 카드: 1개 이상 등록, 사용내역 조회 응답 정상 +- [ ] 홈택스: 매출/매입 세금계산서 수집 응답 정상 +- [ ] 에러 없이 모든 API 호출 성공 + +### 3.3 운영 전환 체크리스트 + +Phase 5 (운영 모드 전환) 전 확인: + +- [ ] 테스트 모드 검증 완료 +- [ ] 고객 동의 (실제 과금 시작 안내) +- [ ] 운영 CERTKEY 설정 확인 +- [ ] 구독 등록 (월정액 과금 설정) +- [ ] `server_mode` → `production` 전환 +- [ ] 운영 모드에서 첫 데이터 수집 성공 확인 + +--- + +## 4. 개발 시 주의사항 + +### 4.1 개발 단계에서의 테스트 모드 + +온보딩 프로세스와 별개로, **서비스 이관 개발 중에는 항상 테스트 모드를 사용**한다. + +| 단계 | 모드 | 이유 | +|------|------|------| +| 서비스 이관 개발 | 테스트 | 코드 검증, 무과금 | +| 베타테스트 | 테스트 | 실사용 시나리오 검증, 무과금 | +| 고객 온보딩 Phase 2~4 | 테스트 | 연동 설정 확인, 무과금 | +| 고객 온보딩 Phase 5~ | **운영** | 실무 사용, 과금 시작 | + +### 4.2 서비스 이관 완료 후 출시 순서 + +``` +1. 서비스 이관 개발 완료 (테스트 모드) +2. 내부 베타테스트 (tenant_id=1, 코드브릿지엑스, 테스트 모드) +3. 외부 베타테스트 (선별 고객 2~3곳, 테스트 모드) +4. 정식 출시 +5. 신규 고객 온보딩 프로세스 가동 (반복) +``` + +--- + +## 관련 문서 + +| 문서 | 설명 | +|------|------| +| [바로빌 연동 시스템](./README.md) | 전체 구조, 테스트/운영 모드, 과금 정책 | +| [바로빌 API 명세](../../frontend/api-specs/barobill-api.md) | REST API 42개 엔드포인트 | +| [데모 테넌트 정책](../sales/demo-tenant-policy.md) | 영업파트너 데모 테넌트 3-Tier 전략 | + +--- + +**최종 업데이트**: 2026-03-17 From 9240034b976abcbdcf34f661dee75c0a7a296ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 17 Mar 2026 09:07:02 +0900 Subject: [PATCH 17/19] =?UTF-8?q?docs:=20[barobill]=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=9C=EC=8B=9C=20=EB=8B=A8=EA=B3=84=EB=B3=84=20?= =?UTF-8?q?=EC=A4=80=EB=B9=84=20=EA=B3=84=ED=9A=8D=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Phase 1~4 로드맵 (SOAP 이관→UI→베타→출시) - 단계별 작업 목록 및 체크리스트 - 바로빌 파트너 정책 확인 필요 사항 - 리스크 및 대응 방안 --- INDEX.md | 1 + dev/dev_plans/barobill-service-launch-plan.md | 242 ++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 dev/dev_plans/barobill-service-launch-plan.md diff --git a/INDEX.md b/INDEX.md index 4be0827..33cb281 100644 --- a/INDEX.md +++ b/INDEX.md @@ -25,6 +25,7 @@ | 급여관리 API | `frontend/api-specs/payroll-api.md` | 급여관리 API 전체 명세 (18개 엔드포인트) | | 바로빌 회계 API | `frontend/api-specs/barobill-api.md` | 카드/은행/홈택스 REST API (42개 엔드포인트) | | 바로빌 시스템 | `features/barobill/README.md` | SOAP 연동, 테스트/운영 모드, 과금, 멀티테넌트, 이관 계획 | +| 바로빌 출시 계획 | `dev/dev_plans/barobill-service-launch-plan.md` | 4단계 출시 로드맵 (SOAP 이관→UI→베타→출시) | | 재공품 생산 정책 | `rules/wip-production-policy.md` | 재공품(WIP) 개념, 제조업 공통 패턴, SAM 처리 방식 | | 재고생산관리 API | `frontend/api-specs/stock-production-api.md` | 재고생산 API 명세 (기존 수주 API + STOCK 타입) | | 결재관리 | `dev/dev_plans/approval-system-unification-plan.md` | MNG→API 결재 통합 계획 | diff --git a/dev/dev_plans/barobill-service-launch-plan.md b/dev/dev_plans/barobill-service-launch-plan.md new file mode 100644 index 0000000..860a120 --- /dev/null +++ b/dev/dev_plans/barobill-service-launch-plan.md @@ -0,0 +1,242 @@ +# 바로빌 서비스 출시 단계별 준비 계획 + +> **작성일**: 2026-03-17 +> **상태**: 계획 수립 +> **담당**: R&D실 + +--- + +## 1. 개요 + +### 1.1 목적 + +MNG에서 운영 중인 바로빌 연동 시스템을 서비스(API+React)로 이관하여, 멀티테넌트 고객이 직접 사용할 수 있는 SaaS 형태로 출시한다. + +### 1.2 현재 상태 + +- **MNG (백오피스)**: 바로빌 SOAP 연동 완료, tenant_id=1 (코드브릿지엑스)에서 실무 운영 중 +- **API**: DB 모델 15개 + REST API 42개 엔드포인트 구현 완료 (데이터 조회/분개용) +- **React**: 바로빌 설정 페이지 기본 구현 + +### 1.3 목표 + +고객(테넌트)이 SAM 서비스에서 바로빌 기능을 직접 설정하고 사용할 수 있도록 한다: +- 계좌조회, 카드내역, 홈택스 세금계산서 자동 수집 +- 전자세금계산서 발행 +- 카카오톡/SMS 알림 + +--- + +## 2. 단계별 로드맵 + +``` +Phase 1 Phase 2 Phase 3 Phase 4 +SOAP 이관 UI 구현 베타테스트 정식 출시 +(API 개발) (React 개발) (내부→외부) (온보딩 가동) +───────────── → ───────────── → ───────────── → ───────────── +``` + +--- + +## 3. Phase 1: SOAP 연동 이관 (API 개발) + +> **핵심**: MNG의 BarobillService를 API로 이관하여 멀티테넌트 지원 + +### 3.1 작업 목록 + +| # | 작업 | 상세 | 난이도 | +|---|------|------|--------| +| 1-1 | BarobillService 이관 | MNG 1,761줄 → API로 이동, 멀티테넌트 리팩토링 | 상 | +| 1-2 | 회원사 관리 API | 등록/수정/조회/상태확인 엔드포인트 | 중 | +| 1-3 | 인증서 관리 API | 등록URL/유효성/만료일 조회 엔드포인트 | 중 | +| 1-4 | 계좌 관리 API | 등록/목록/입출금 조회 엔드포인트 | 중 | +| 1-5 | 카드 관리 API | 등록/수정/해지/사용내역 조회 엔드포인트 | 중 | +| 1-6 | 세금계산서 발행 API | 작성/발행/조회 엔드포인트 | 상 | +| 1-7 | 동기화 스케줄러 | 은행/카드/홈택스 자동 수집 (Queue Job) | 중 | +| 1-8 | 테스트/운영 모드 전환 API | 회원사별 server_mode 전환 | 하 | + +### 3.2 기술 과제 + +| 과제 | 설명 | 대응 방안 | +|------|------|----------| +| CERTKEY 관리 | 현재 전역 1개 → 멀티테넌트 대응 필요 | 바로빌 파트너 계약 구조 확인 후 결정 | +| PHP SOAP 확장 | API 서버에 `php-soap` 설치 필요 | Docker/서버 환경 확인 | +| 암호화 키 공유 | MNG/API 간 `APP_KEY` 동일해야 복호화 가능 | 현재 동일 키 사용 중 (확인 필요) | +| 동기화 부하 | 테넌트 수 증가 시 SOAP 호출량 증가 | Queue 분산, 호출 간격 조절 | + +### 3.3 환경 준비 + +```bash +# API 서버에 PHP SOAP 확장 확인 +php -m | grep soap + +# 없으면 설치 (개발 서버 Level 2) +sudo apt install php8.4-soap +sudo systemctl restart php8.4-fpm + +# .env 설정 추가 +BAROBILL_CERT_KEY_TEST=<테스트 인증키> +BAROBILL_CERT_KEY_PROD=<운영 인증키> +BAROBILL_CORP_NUM=<파트너 사업자번호> +BAROBILL_TEST_MODE=true +``` + +--- + +## 4. Phase 2: UI 구현 (React 개발) + +> **핵심**: 고객이 직접 바로빌을 설정하고 데이터를 조회할 수 있는 화면 + +### 4.1 작업 목록 + +| # | 작업 | 상세 | 난이도 | +|---|------|------|--------| +| 2-1 | 바로빌 설정 페이지 | 회원사 등록/수정, 서버 모드 표시 | 중 | +| 2-2 | 인증서 관리 화면 | 등록 URL 안내, 유효기간 표시, 갱신 알림 | 중 | +| 2-3 | 계좌 관리 화면 | 등록 계좌 목록, 등록 URL 안내 | 중 | +| 2-4 | 카드 관리 화면 | 등록 카드 목록, 추가/해지 | 중 | +| 2-5 | 카드 거래내역 조회 | 기간별 조회, 분개 연동, 숨김/분할 | 상 | +| 2-6 | 은행 거래내역 조회 | 기간별 조회, 분개 연동, 오버라이드/분할 | 상 | +| 2-7 | 홈택스 세금계산서 | 매출/매입 조회, 분개 연동 | 중 | +| 2-8 | 세금계산서 발행 화면 | 작성/발행 폼, 미리보기 | 상 | + +### 4.2 화면 구성 (메뉴 구조) + +``` +재무관리 +├─ 계좌관리 +│ ├─ 보유계좌 관리 (바로빌 계좌 등록 포함) +│ └─ 계좌 입출금 내역 +├─ 카드관리 +│ ├─ 법인카드 관리 (바로빌 카드 등록 포함) +│ └─ 카드 사용내역 +├─ 세금계산서 +│ ├─ 매출 세금계산서 +│ ├─ 매입 세금계산서 +│ └─ 세금계산서 발행 +└─ 설정 + └─ 바로빌 연동 설정 (인증서, 모드, 충전잔액) +``` + +--- + +## 5. Phase 3: 베타테스트 + +> **핵심**: 내부 → 외부 순서로 검증, 테스트 모드 사용 + +### 5.1 내부 베타테스트 + +| 항목 | 내용 | +|------|------| +| **대상** | tenant_id=1 (코드브릿지엑스 본사) | +| **기간** | 2주 | +| **모드** | 테스트 모드 | +| **검증 항목** | 전체 기능 동작, UI/UX, 데이터 정합성 | +| **비교 기준** | MNG 운영 데이터와 서비스 데이터 일치 확인 | + +**내부 베타 체크리스트**: + +- [ ] 회원사 등록/수정 정상 동작 +- [ ] 인증서 등록 URL 정상 접근 +- [ ] 계좌 등록 및 입출금 내역 조회 +- [ ] 카드 등록 및 사용내역 조회 +- [ ] 홈택스 매출/매입 세금계산서 수집 +- [ ] 세금계산서 발행 (테스트 서버) +- [ ] 분개 연동 정상 동작 +- [ ] 동기화 스케줄러 자동 수집 확인 +- [ ] MNG 데이터와 서비스 데이터 일치 + +### 5.2 외부 베타테스트 + +| 항목 | 내용 | +|------|------| +| **대상** | 선별 고객사 2~3곳 | +| **기간** | 2~4주 | +| **모드** | 테스트 모드 | +| **검증 항목** | 실사용 시나리오, 다양한 사업자 유형, 피드백 수집 | + +**외부 베타 체크리스트**: + +- [ ] 다양한 사업자번호로 회원 등록 +- [ ] 다양한 은행/카드사 연동 확인 +- [ ] 고객 직접 인증서/계좌/카드 등록 가능 확인 +- [ ] 고객 피드백 수집 및 반영 +- [ ] 성능 (다수 테넌트 동시 동기화) + +--- + +## 6. Phase 4: 정식 출시 + +> **핵심**: 운영 모드 전환, 과금 시작, 온보딩 프로세스 가동 + +### 6.1 출시 준비 체크리스트 + +**인프라**: +- [ ] API 서버 `php-soap` 확장 설치 확인 +- [ ] 운영 `.env`에 `BAROBILL_CERT_KEY_PROD`, `BAROBILL_CORP_NUM` 설정 +- [ ] `BAROBILL_TEST_MODE=false` 설정 +- [ ] 동기화 스케줄러 Supervisor 등록 +- [ ] 바로빌 운영 CERTKEY 충전잔액 확보 + +**과금**: +- [ ] `barobill_pricing_policies` 요금 정책 데이터 입력 +- [ ] 월정액 구독 자동 과금 배치 등록 (매월 1일) +- [ ] 과금 내역 고객 조회 화면 (선택) + +**운영**: +- [ ] 인증서 만료 알림 (이메일/카카오톡) +- [ ] 충전잔액 부족 알림 +- [ ] 동기화 실패 알림 및 재시도 로직 +- [ ] 바로빌 장애 시 대응 매뉴얼 + +### 6.2 온보딩 프로세스 정립 + +정식 출시 후 신규 고객 가입 시: + +``` +계약 → 테넌트 생성 → 회원등록(테스트) → 인증서/계좌/카드 → 검증 → 운영전환 → 실무사용 +``` + +> 상세 프로세스: `features/barobill/tenant-onboarding.md` 참조 + +--- + +## 7. 바로빌 파트너 정책 확인 필요 사항 + +> **경고: 개발 착수 전 바로빌 측에 확인해야 할 사항** + +| # | 확인 사항 | 이유 | 현재 상태 | +|---|----------|------|----------| +| 1 | 멀티테넌트 CERTKEY 구조 | 파트너 1개 키로 다수 회원사 관리 가능한지 | 미확인 | +| 2 | 테스트 서버 제한 | 테스트 API 호출 횟수/기간 제한 | 미확인 | +| 3 | 과금 구조 | 파트너 단가표 (건당/월정액) | 미확인 | +| 4 | SLA | 바로빌 API 가용성 보장 수준 | 미확인 | +| 5 | 회원사 대량 등록 | 일괄 등록 API 또는 제한 | 미확인 | +| 6 | 인증서 대리 등록 | 고객 대신 등록 가능 여부 | 미확인 | + +--- + +## 8. 리스크 및 대응 + +| 리스크 | 영향 | 대응 | +|--------|------|------| +| 바로빌 API 장애 | 거래 데이터 수집 중단 | 재시도 로직 + 장애 알림 | +| 인증서 만료 | 계좌/세금계산서 조회 불가 | 만료 30일 전 알림 | +| SOAP 호출 지연 | 페이지 응답 지연 | 비동기 Queue 처리 | +| 테넌트 급증 | 동기화 부하 | 호출 간격 분산, 우선순위 큐 | +| 충전잔액 부족 | API 호출 실패 | 잔액 모니터링 + 자동 알림 | + +--- + +## 관련 문서 + +| 문서 | 설명 | +|------|------| +| [바로빌 연동 시스템](../../features/barobill/README.md) | 전체 구조, 모드, 과금 | +| [테넌트 온보딩](../../features/barobill/tenant-onboarding.md) | 온보딩 6단계 프로세스 | +| [바로빌 API 명세](../../frontend/api-specs/barobill-api.md) | REST API 42개 엔드포인트 | +| [이관 현황](../../system/migration-status.md) | MNG→API+React 전체 이관 현황 | + +--- + +**최종 업데이트**: 2026-03-17 From d9fb01c213b03ad068a1b0cbb0bcae0798ce1a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 17 Mar 2026 09:10:02 +0900 Subject: [PATCH 18/19] =?UTF-8?q?docs:=20[barobill]=20CEO=20=EC=84=A4?= =?UTF-8?q?=EB=AA=85=EC=9A=A9=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=B6=9C?= =?UTF-8?q?=EC=8B=9C=20=EA=B3=84=ED=9A=8D=20PPTX=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 7장 슬라이드 (표지, 현황, 로드맵, 모드, 온보딩, 과금, Next Steps) - 4단계 출시 로드맵 시각화 - 테스트/운영 모드 비교 - 과금 구조 및 수익 시뮬레이션 --- .../barobill-service-launch-plan.pptx | Bin 0 -> 267568 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dev/dev_plans/barobill-service-launch-plan.pptx diff --git a/dev/dev_plans/barobill-service-launch-plan.pptx b/dev/dev_plans/barobill-service-launch-plan.pptx new file mode 100644 index 0000000000000000000000000000000000000000..975631d6b73479202c175b78751bf94a6c4f7c48 GIT binary patch literal 267568 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 literal 0 HcmV?d00001 From 4bb22609cd0d0c2fd6ef60f6c32a37064140c358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 17 Mar 2026 11:29:44 +0900 Subject: [PATCH 19/19] =?UTF-8?q?docs:=20[changes]=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=EC=83=9D=EC=82=B0=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=9D=B4=EB=A0=A5=20=EC=B6=94=EA=B0=80=20(2026-03-17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - $isStock/$process 미정의 오류 수정 - 수량 정수 변환 (API + React) --- .../20260316_stock_production_order.md | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/dev/changes/20260316_stock_production_order.md b/dev/changes/20260316_stock_production_order.md index 7a72b1f..be34261 100644 --- a/dev/changes/20260316_stock_production_order.md +++ b/dev/changes/20260316_stock_production_order.md @@ -115,16 +115,50 @@ if ($status === Order::STATUS_CONFIRMED --- +## 버그 수정 (2026-03-17) + +### 커밋 3: `fix: [production] 생산지시 생성 시 $isStock 미정의 오류 및 수량 정수 변환` + +| 파일 | 변경 내용 | +|------|----------| +| `app/Services/OrderService.php` | `DB::transaction` 클로저 `use`절에 `$isStock` 변수 추가 | +| `app/Services/OrderService.php` | `work_order_items.quantity`를 `(int)` 캐스팅하여 정수로 저장 | + +**원인**: `$isStock`가 line 1242에서 정의되지만, `DB::transaction` 클로저의 `use`절에 포함되지 않아 클로저 내부에서 참조 불가 → 500 에러 발생 + +### 커밋 4: `fix: [production] 생산지시 생성 시 $process 미정의 오류 수정` + +| 파일 | 변경 내용 | +|------|----------| +| `app/Services/OrderService.php` | `$process = null;` 초기화 추가 (if 블록 밖에서도 참조 가능하도록) | + +**원인**: `$process`가 `if ($processId)` 블록 안에서만 정의되지만, 블록 밖 line 1420에서 team_id 결정 시 참조 → 공정 없는 품목 처리 시 500 에러 발생 + +### 커밋 5 (React): `fix: [stocks] 재고생산 수량을 정수로 표시` + +| 파일 | 변경 내용 | +|------|----------| +| `src/components/stocks/actions.ts` | `transformItemApiToFrontend`에서 `Math.floor(Number())` 적용 | +| `src/components/stocks/actions.ts` | 금액 필드도 `Number()`로 안전한 형변환 적용 | + +**원인**: API의 Eloquent `decimal:4` 캐스트가 수량을 `"1.0000"` 문자열로 반환하여 프론트엔드에서 소수점 그대로 표시 + +--- + ## 테스트 체크리스트 - [x] STOCK 수주 생성 → `order_no` STK 접두사 확인 - [x] STOCK 수주 생성 → `site_name='재고생산'` 자동 설정 확인 - [ ] STOCK 수주 확정 → 매출 자동 생성 안 됨 확인 -- [ ] STOCK 생산지시 생성 → 절곡 공정 자동 선택 확인 -- [ ] STOCK 생산지시 생성 → `project_name='재고생산'` 확인 -- [ ] STOCK 생산지시 생성 → `scheduled_date=today` 확인 +- [x] STOCK 생산지시 생성 → 절곡 공정 자동 선택 확인 +- [x] STOCK 생산지시 생성 → `project_name='재고생산'` 확인 +- [x] STOCK 생산지시 생성 → `scheduled_date=today` 확인 - [ ] 기존 ORDER 수주 생산지시 → 기존 BOM 매칭 정상 동작 확인 - [ ] 생산지시 목록에서 STOCK 건 표시 확인 +- [x] 생산지시 생성 시 `$isStock` 미정의 오류 수정 확인 +- [x] 생산지시 생성 시 `$process` 미정의 오류 수정 확인 +- [x] `work_order_items.quantity` 정수 저장 확인 +- [x] 프론트엔드 수량 정수 표시 확인 --- @@ -136,4 +170,4 @@ if ($status === Order::STATUS_CONFIRMED --- -**최종 업데이트**: 2026-03-16 +**최종 업데이트**: 2026-03-17