@@ -2,7 +2,6 @@
import { computed , onMounted , ref , watch } from 'vue' ;
import axios from 'axios' ;
import { api } from '@/lib/api' ;
import { formatMoney } from '@/lib/money' ;
import {
dateToJalali ,
formatClockFa ,
@@ -79,8 +78,171 @@ const currentLink = computed(() =>
const hasMultipleConnections = computed ( ( ) => connections . value . length > 1 ) ;
const goldBalance = computed ( ( ) => assets . value ? . goldBalance ? ? null ) ;
const rialBalance = computed ( ( ) => assets . value ? . rialBalance ? ? null ) ;
/** برچسب فارسی فیلدهای شناختهشدهٔ پاسخ مانده؛ کلیدهای ناشناخته با همان نام خام نمایش داده میشوند */
const MANDEH _FIELD _LABELS : Record < string , string > = {
Code : 'کد حساب' ,
MandeyeMali : 'مانده مالی' ,
MandeyeVazni : 'مانده وزنی (طلا)' ,
MandeyeVazniNoghre : 'مانده وزنی نقره' ,
MandeMenhayeSeke : 'مانده منهای سکه' ,
MandeyeKolBeTALA : 'مانده کل به طلا' ,
MandeyeKolBePool : 'مانده کل به پول' ,
} ;
function mandehFieldLabel ( key : string ) {
if ( MANDEH _FIELD _LABELS [ key ] ) return MANDEH _FIELD _LABELS [ key ] ! ;
if ( key . startsWith ( 'Seke_' ) ) return ` سکه ${ key . slice ( 5 ) } ` ;
if ( key . startsWith ( 'MandeyeArz_' ) ) return ` مانده ارز ${ key . slice ( 11 ) } ` ;
return key ;
}
/** فقط قالببندی نمایشی (جداکنندهٔ هزارگان)؛ مقدار و علامت همان پاسخ تهحساب است */
function faDigits ( s : string ) {
return s . replace ( /\d/g , ( d ) => '۰۱۲۳۴۵۶۷۸۹' [ Number ( d ) ] ! ) ;
}
function showValue ( v : unknown ) : string {
const s = dash ( v , '' ) ;
if ( ! s ) return '—' ;
if ( /^-?\d+(\.\d+)?$/ . test ( s ) ) {
const [ int , frac ] = s . replace ( '-' , '' ) . split ( '.' ) ;
const grouped = int ! . replace ( /\B(?=(\d{3})+(?!\d))/g , ',' ) ;
return ` ${ s . startsWith ( '-' ) ? '-' : '' } ${ faDigits ( grouped ) } ${ frac ? ` ٫ ${ faDigits ( frac ) } ` : '' } ` ;
}
return s ;
}
/** شناسهها/کدها بدون جداکنندهٔ هزارگان */
function showRaw ( v : unknown ) : string {
const s = dash ( v , '' ) ;
return s ? faDigits ( s ) : '—' ;
}
const mandehFields = computed ( ( ) => assets . value ? . mandehFields ? ? null ) ;
const mandehDetailRaw = computed ( ( ) => assets . value ? . mandehDetailRaw ? ? null ) ;
const DETAIL _VALUE _COLS = [
{ key : 'Value' , label : 'مقدار' } ,
{ key : 'Value1' , label : 'وزن (Value1)' } ,
{ key : 'Value2' , label : 'تعداد (Value2)' } ,
{ key : 'Value3' , label : 'ارزی (Value3)' } ,
{ key : 'Value4' , label : 'ریالی (Value4)' } ,
] as const ;
const detailCols = computed ( ( ) =>
DETAIL _VALUE _COLS . filter ( ( c ) => ( mandehDetailRaw . value ? ? [ ] ) . some ( ( d ) => d [ c . key ] != null ) ) ,
) ;
/** برچسب فیلدهای سند تهحساب (DoListAsnad) */
const ASNAD _FIELD _LABELS : Record < string , string > = {
NO : 'نوع' ,
Radif : 'ردیف' ,
Vazn : 'وزن' ,
Ayar : 'عیار' ,
TabdilVazn : 'وزن تبدیلی' ,
Count : 'تعداد' ,
No _Kar : 'نوع کار' ,
Fi : 'فی' ,
Mazaneh : 'مظنه' ,
Mali : 'مالی' ,
Arz : 'ارز' ,
Name _Bank : 'بانک' ,
Tarikh _Cheq : 'تاریخ چک' ,
Sh _Cheq : 'شماره چک' ,
Sh _Posht _Check : 'شماره پشت چک' ,
Operation _Date : 'تاریخ عملیات' ,
Sh _Sharti : 'شماره شرطی' ,
Name _az : 'آزمایشگاه' ,
Baabat : 'بابت' ,
Sharh1 : 'شرح' ,
Sharh2 : 'شرح ۲' ,
TasirBarMande : 'تأثیر بر مانده' ,
TahesabVazni : 'حساب وزنی' ,
TahesabMali : 'حساب مالی' ,
TahesabArzi : 'حساب ارزی' ,
Jens _Felez : 'جنس فلز' ,
Seller : 'فروشنده' ,
User : 'کاربر ثبت' ,
Tarikh _Time : 'ساعت' ,
ZamanSabt : 'زمان ثبت' ,
ZamanEdit : 'زمان ویرایش' ,
isSanadMovaghat : 'سند موقت' ,
MCode : 'کد مشتری' ,
Factor _Code : 'کد فاکتور' ,
ID : 'شناسه' ,
} ;
const ASNAD _ORDER = Object . keys ( ASNAD _FIELD _LABELS ) ;
/** در سرتیتر سند/ردیف نمایش داده میشوند */
const ASNAD _HEAD _KEYS = new Set ( [ 'Sh_Factor' , 'Sh_factor' , 'Tarikh' , 'NO' ] ) ;
/** مقادیر کمّی که جداکنندهٔ هزارگان میگیرند؛ بقیه (کدها، شناسهها، عیار، تاریخ) خام */
const ASNAD _AMOUNT _KEYS = new Set ( [ 'Vazn' , 'TabdilVazn' , 'Mali' , 'Fi' , 'Mazaneh' , 'Count' ] ) ;
function asnadFields ( item : Record < string , unknown > ) {
const keys = Object . keys ( item ) . filter ( ( k ) => ! ASNAD _HEAD _KEYS . has ( k ) ) ;
keys . sort ( ( a , b ) => {
const ia = ASNAD _ORDER . indexOf ( a ) ;
const ib = ASNAD _ORDER . indexOf ( b ) ;
return ( ia < 0 ? 999 : ia ) - ( ib < 0 ? 999 : ib ) ;
} ) ;
return keys
. map ( ( k ) => {
const v = item [ k ] ;
if ( typeof v === 'boolean' ) return { key : k , label : ASNAD _FIELD _LABELS [ k ] ? ? k , value : v ? 'بله' : 'خیر' } ;
if ( v != null && typeof v === 'object' ) return null ;
const s = dash ( v , '' ) ;
if ( ! s ) return null ;
return { key : k , label : ASNAD _FIELD _LABELS [ k ] ? ? k , value : asnadCell ( k , s ) } ;
} )
. filter ( ( x ) : x is { key : string ; label : string ; value : string } => x != null ) ;
}
/** کدهای جنس فلز طبق مستندات تهحساب */
const JENS _FELEZ _LABELS : Record < string , string > = { '0' : 'طلا' , '1' : 'پلاتین' , '2' : 'نقره' } ;
function asnadCell ( k : string , v : unknown ) : string {
const s = dash ( v , '' ) ;
if ( ! s ) return '—' ;
if ( k === 'Jens_Felez' ) return JENS _FELEZ _LABELS [ s ] ? ? showRaw ( s ) ;
if ( typeof v === 'boolean' ) return v ? 'بله' : 'خیر' ;
return ASNAD _AMOUNT _KEYS . has ( k ) ? showValue ( s ) : showRaw ( s ) ;
}
function isZeroish ( v : unknown ) {
const s = dash ( v , '' ) ;
return ! s || /^-?0+(\.0+)?$/ . test ( s ) ;
}
/** جنس فلز فقط برای ردیفهای وزنی معنا دارد؛ ردیفهای صرفاً مالی هم Jens_Felez=0 دارند */
function rowMetal ( r : Record < string , unknown > ) {
if ( isZeroish ( r . Vazn ) && isZeroish ( r . TabdilVazn ) ) return '—' ;
return asnadCell ( 'Jens_Felez' , r . Jens _Felez ) ;
}
const INVOICE _COLS = [
{ key : 'Count' , label : 'تعداد' } ,
{ key : 'Vazn' , label : 'وزن' } ,
{ key : 'Ayar' , label : 'عیار' } ,
{ key : 'TabdilVazn' , label : 'وزن تبدیلی' } ,
{ key : 'Fi' , label : 'فی' } ,
{ key : 'Mazaneh' , label : 'مظنه' } ,
{ key : 'Mali' , label : 'مالی' } ,
{ key : 'Arz' , label : 'ارز' } ,
] as const ;
const INVOICE _HEAD _KEYS = [ 'Tarikh_Time' , 'MCode' , 'User' , 'isSanadMovaghat' ] as const ;
const INVOICE _SHOWN _KEYS = new Set < string > ( [
... ASNAD _HEAD _KEYS ,
... INVOICE _COLS . map ( ( c ) => c . key ) ,
... INVOICE _HEAD _KEYS ,
'No_Kar' ,
'Sharh1' ,
'Jens_Felez' ,
] ) ;
function invoiceCols ( rows : Record < string , unknown > [ ] ) {
return INVOICE _COLS . filter ( ( c ) => rows . some ( ( r ) => ! isZeroish ( r [ c . key ] ) ) ) ;
}
function invoiceExtra ( r : Record < string , unknown > ) {
return asnadFields ( r ) . filter ( ( f ) => ! INVOICE _SHOWN _KEYS . has ( f . key ) ) ;
}
const fetchedAtLabel = computed ( ( ) => {
const raw = assets . value ? . fetchedAt ;
@@ -90,57 +252,6 @@ const fetchedAtLabel = computed(() => {
return ` ${ formatJalaliDay ( d ) } ${ formatClockFa ( d ) } ` ;
} ) ;
const mandehGroups = computed ( ( ) => {
const items = assets . value ? . mandehDetail ? ? [ ] ;
const groups : Record < string , typeof items > = {
مالی : [ ] ,
'فلزات/وزنی' : [ ] ,
سکه : [ ] ,
کالا : [ ] ,
ارز : [ ] ,
سایر : [ ] ,
} ;
for ( const item of items ) {
const name = item . Name || '' ;
if ( /ریال|تومان|مالی|پول|وجه/i . test ( name ) ) groups [ 'مالی' ] ! . push ( item ) ;
else if ( /سکه/i . test ( name ) ) groups [ 'سکه' ] ! . push ( item ) ;
else if ( /ارز|دلار|یورو/i . test ( name ) ) groups [ 'ارز' ] ! . push ( item ) ;
else if ( /طلا|نقره|پلاتین|وزن|آبشده|شمش/i . test ( name ) )
groups [ 'فلزات/وزنی' ] ! . push ( item ) ;
else if ( /کار|اتیکت|کالا/i . test ( name ) ) groups [ 'کالا' ] ! . push ( item ) ;
else groups [ 'سایر' ] ! . push ( item ) ;
}
return Object . entries ( groups )
. filter ( ( [ , list ] ) => list . length > 0 )
. map ( ( [ label , list ] ) => ( { label , items : list } ) ) ;
} ) ;
function balanceTone ( v : number | null ) {
if ( v == null || v === 0 ) return 'tone-neutral' ;
return v > 0 ? 'tone-credit' : 'tone-debt' ;
}
function balanceLabel ( v : number | null ) {
if ( v == null || v === 0 ) return '' ;
return v > 0 ? 'بستانکار' : 'بدهکار' ;
}
function formatAmt ( v : number | null , digits = 3 ) {
if ( v == null ) return '—' ;
return Math . abs ( v ) . toLocaleString ( 'fa-IR' , {
maximumFractionDigits : digits ,
minimumFractionDigits : 0 ,
} ) ;
}
function formatMandehAmount ( amount : string | number , unit : string ) {
const n = Number ( amount ) ;
if ( ! Number . isFinite ( n ) ) return String ( amount ) ;
const abs = Math . abs ( n ) ;
if ( unit === 'ریال' ) return formatMoney ( abs , 'IRR' ) ;
return abs . toLocaleString ( 'fa-IR' , { maximumFractionDigits : 3 } ) ;
}
/** تهحساب گاهی رشتهٔ "null" میفرستد؛ ?? کافی نیست */
function dash ( v : unknown , fallback = '—' ) : string {
if ( v == null ) return fallback ;
@@ -149,6 +260,39 @@ function dash(v: unknown, fallback = '—'): string {
return s ;
}
type AsnadView = 'grouped' | 'rows' ;
const ASNAD _VIEW _KEY = 'mento.asnadView' ;
const asnadView = ref < AsnadView > (
localStorage . getItem ( ASNAD _VIEW _KEY ) === 'rows' ? 'rows' : 'grouped' ,
) ;
watch ( asnadView , ( v ) => localStorage . setItem ( ASNAD _VIEW _KEY , v ) ) ;
function sanadNo ( item : Record < string , unknown > ) {
return dash ( item . Sh _Factor ? ? item . Sh _factor , '' ) ;
}
/** تهحساب هر ردیف سند را جدا برمیگرداند؛ ردیفهای همشماره و همتاریخ یک سند هستند (فقط دستهبندی، بدون محاسبه) */
const asnadGroups = computed ( ( ) => {
const map = new Map < string , { no : string ; date : string ; rows : Record < string , unknown > [ ] } > ( ) ;
for ( const item of asnadItems . value ) {
const no = sanadNo ( item ) ;
const date = dash ( item . Tarikh , '' ) ;
const key = ` ${ no } | ${ date } ` ;
let g = map . get ( key ) ;
if ( ! g ) {
g = { no , date , rows : [ ] } ;
map . set ( key , g ) ;
}
g . rows . push ( item ) ;
}
return [ ... map . values ( ) ] . map ( ( g ) => ( {
key : ` ${ g . no } | ${ g . date } ` ,
no : g . no || '—' ,
date : g . date || '—' ,
rows : g . rows ,
} ) ) ;
} ) ;
/** تبدیل تاریخ محلی میلادی به شمسی YYYY-MM-DD برای API تهحساب */
function toShamsiYmd ( localIso : string ) : string | null {
const d = parseLocalInput ( localIso ) ;
@@ -488,38 +632,26 @@ onMounted(async () => {
< template v-if = "currentLink && assets" >
< section class = "summary card" >
< h3 > دارایی < / h3 >
< div class = "balance-grid" >
< article class = "balance-box" :class = "balanceTone(goldBalance) ">
< span > دارایی طلایی < / span >
< strong dir = "ltr" >
{ { formatAmt ( goldBalance ) } }
< em > ( گرم ) < / em >
< / strong >
< small v-if = "balanceLabel(goldBalance)" > {{ balanceLabel ( goldBalance ) }} < / small >
< / article >
< article class = "balance-box" :class = "balanceTone(rialBalance)" >
< span > دارایی ریالی < / span >
< strong dir = "ltr" >
{ { rialBalance != null ? formatMoney ( Math . abs ( rialBalance ) , 'IRR' ) : '—' } }
< / strong >
< small v-if = "balanceLabel(rialBalance)" > {{ balanceLabel ( rialBalance ) }} < / small >
< / article >
< article
v-if = "assets.totalGoldEquiv != null"
class = "balance-box"
:class = "balanceTone(assets.totalGoldEquiv)"
>
< span > دارایی کل به طلا < / span >
< strong dir = "ltr" >
{ { formatAmt ( assets . totalGoldEquiv ) } }
< em > ( گرم ) < / em >
< / strong >
< small v-if = "balanceLabel(assets.totalGoldEquiv)" > {{
balanceLabel ( assets.totalGoldEquiv )
}} < / small >
< h3 > مانده حساب ( طبق ته حساب ) < / h3 >
< div v-if = "mandehFields?.length" class=" balance-grid" >
< article v-for = "f in mandehFields" :key="f.key" class="balance-box " >
< span > { { mandehFieldLabel ( f . key ) } } < / span >
< strong dir = "ltr" > { { showValue ( f . value ) } } < / strong >
< / article >
< / div >
< div v-else class = "balance-grid" >
< article class = "balance-box" >
< span > مانده وزنی ( طلا ) < / span >
< strong dir = "ltr" > { { showValue ( assets . goldBalance ) } } < / strong >
< / article >
< article class = "balance-box" >
< span > مانده مالی < / span >
< strong dir = "ltr" > { { showValue ( assets . rialBalance ) } } < / strong >
< / article >
< / div >
< p class = "muted tiny" >
اعداد دقیقا ً همان مقادیر ته حساب هستند ( با همان علامت مثبت / منفی ) و CRM هیچ محاسبه یا تبدیلی روی آن ها انجام نمی دهد .
< / p >
< p v-if = "fetchedAtLabel" class="muted tiny" > آخرین دریافت : {{ fetchedAtLabel }} < / p >
< / section >
@@ -537,31 +669,37 @@ onMounted(async () => {
< / div >
< div v-show = "activeTab === 'مانده حساب'" class="detail" >
< template v-for = "group in mandehGroups" :key="group.label " >
< div class = "group-title " >
< span class = "line" / >
< strong > { { group . label } } < / strong >
< span class = "line" / >
< div v-if = "mandehDetailRaw?.length" class="raw-table" role="table " >
< div class = "raw-tr raw-th" role = "row" : style = "{ '--cols': detailCols.length } ">
< span role = "columnheader" > عنوان < / span >
< span v-for = "c in detailCols" :key="c.key" role="columnheader" > {{ c.label }} < / span >
< / div >
< div
v-for = "(item, idx) in group.items "
:key = "idx "
class = "mandeh-card card "
v-for = "(d, di) in mandehDetailRaw "
:key = "d i"
class = "raw-tr "
role = "row"
: style = "{ '--cols': detailCols.length }"
>
< span role = "cell" class = "raw-name" > { { d . Name } } < / span >
< span v-for = "c in detailCols" :key="c.key" role="cell" dir="ltr" class="raw-num" >
{{ showValue ( d [ c.key ] ) }}
< / span >
< / div >
< / div >
< template v-else-if = "assets.mandehDetail?.length" >
< div v-for = "(item, idx) in assets.mandehDetail" :key="idx" class="mandeh-card card" >
< div class = "name" > { { item . Name } } : < / div >
< div class = "vals" >
< template v-for = "(v, vi) in item.values" :key="vi" >
< span : class = "Number(v.amount) >= 0 ? 'credit' : 'debt'" >
{ { formatMandehAmount ( v . amount , v . unit ) } }
< / span >
< span dir = "ltr" > { { showValue ( v . amount ) } } < / span >
< span class = "unit" > { { v . unit } } < / span >
< span class = "st" > { { Number ( v . amount ) >= 0 ? 'بد' : 'بس' } } < / span >
< span v-if = "vi < item.values.length - 1" class="sep" > | < / span >
< / template >
< / div >
< / div >
< / template >
< p v-if = "!mandehGroups.length" class= "muted"> جزئیات ماندهای نیست. < / p >
< p v-else class = "muted" > جزئیات مانده ای نیست . < / p >
< / div >
< div v-show = "activeTab === 'جزئیات حساب'" class="asnad" >
@@ -578,7 +716,101 @@ onMounted(async () => {
{{ asnadLoading ? ' … ' : ' نمایش اسناد ' }}
< / button >
< / div >
< div class = "asnad-grid " >
< div v-if = "asnadItems.length" class="view-bar " >
< span class = "muted" >
{ { asnadGroups . length . toLocaleString ( 'fa-IR' ) } } سند ·
{ { asnadItems . length . toLocaleString ( 'fa-IR' ) } } ردیف
< / span >
< div class = "seg" role = "tablist" aria -label = " نحوه نمایش اسناد " >
< button
type = "button"
: class = "{ on: asnadView === 'grouped' }"
: aria -selected = " asnadView = = = ' grouped ' "
@click ="asnadView = 'grouped'"
>
هر سند یکجا
< / button >
< button
type = "button"
: class = "{ on: asnadView === 'rows' }"
: aria -selected = " asnadView = = = ' rows ' "
@click ="asnadView = 'rows'"
>
ردیف به ردیف
< / button >
< / div >
< / div >
< div v-if = "asnadView === 'grouped'" class="asnad-groups" >
< article v-for = "g in asnadGroups" :key="g.key" class="invoice card" >
< header class = "inv-head" >
< div class = "inv-title" >
< strong > سند شماره { { showRaw ( g . no ) } } < / strong >
< span v-if = "g.rows[0]?.isSanadMovaghat === true" class="inv-badge" > موقت < / span >
< span class = "sg-count" > { { g . rows . length . toLocaleString ( 'fa-IR' ) } } ردیف < / span >
< / div >
< dl class = "inv-meta" >
< div >
< dt > تاریخ < / dt >
< dd > { { showRaw ( g . date ) } } < / dd >
< / div >
< div v-for = "k in INVOICE_HEAD_KEYS.filter((x) => x !== 'isSanadMovaghat' && dash(g.rows[0]?.[x], ''))" :key="k" >
< dt > { { ASNAD _FIELD _LABELS [ k ] } } < / dt >
< dd > { { asnadCell ( k , g . rows [ 0 ] ? . [ k ] ) } } < / dd >
< / div >
< / dl >
< / header >
< div class = "inv-table-wrap" >
< table class = "inv-table" >
< thead >
< tr >
< th > # < / th >
< th class = "inv-desc" > شرح < / th >
< th > جنس فلز < / th >
< th v-for = "c in invoiceCols(g.rows)" :key="c.key" > {{ c.label }} < / th >
< / tr >
< / thead >
< tbody >
< template v-for = "(r, ri) in g.rows" :key="String(r.Factor_Code ?? r.ID ?? ri)" >
< tr >
< td class = "inv-idx" > { { showRaw ( r . Radif ? ? ri + 1 ) } } < / td >
< td class = "inv-desc" >
< strong > { { dash ( r . NO ) } } < / strong >
< small v-if = "dash(r.No_Kar, '')" > {{ dash ( r.No_Kar ) }} < / small >
< small v-if = "dash(r.Sharh1, '')" > {{ dash ( r.Sharh1 ) }} < / small >
< / td >
< td > { { rowMetal ( r ) } } < / td >
< td v-for = "c in invoiceCols(g.rows)" :key="c.key" class="inv-num" >
< bdi dir = "ltr" > { { asnadCell ( c . key , r [ c . key ] ) } } < / bdi >
< / td >
< / tr >
< tr v-if = "invoiceExtra(r).length" class="inv-extra-tr" >
< td : colspan = "3 + invoiceCols(g.rows).length" >
< details >
< summary > سایر جزئیات ته حساب < / summary >
< dl class = "kv" >
< div
v-for = "f in invoiceExtra(r)"
:key = "f.key"
class = "kv-item"
:class = "`k-${f.key}`"
>
< dt > { { f . label } } < / dt >
< dd > { { f . value } } < / dd >
< / div >
< / dl >
< / details >
< / td >
< / tr >
< / template >
< / tbody >
< / table >
< / div >
< / article >
< / div >
< div v-else class = "asnad-grid" >
< article
v-for = "(item, idx) in asnadItems"
: key = "String(item.Factor_Code ?? item.ID ?? idx)"
@@ -606,6 +838,9 @@ onMounted(async () => {
< / div >
< / article >
< / div >
< p v-if = "asnadItems.length && asnadView === 'grouped'" class="muted tiny" >
همهٔ فیلدهایی که تهحساب برای هر ردیف برگردانده نمایش داده میشود ؛ فیلدهای خالی حذف شدهاند و هیچ جمع یا تبدیلی در CRM انجام نمیشود.
< / p >
< p v-if = "!asnadLoading && !asnadItems.length" class="muted" >
در این بازه سندی یافت نشد.
< / p >
@@ -906,12 +1141,165 @@ onMounted(async () => {
gap : 0.65 rem ;
grid - template - columns : repeat ( auto - fill , minmax ( 260 px , 1 fr ) ) ;
}
. view - bar {
display : flex ;
align - items : center ;
justify - content : space - between ;
gap : 0.5 rem ;
flex - wrap : wrap ;
margin - bottom : 0.65 rem ;
}
. seg {
display : inline - flex ;
padding : 3 px ;
gap : 3 px ;
border : 1 px solid var ( -- line ) ;
border - radius : var ( -- radius ) ;
background : var ( -- bg ) ;
}
. seg button {
border : 0 ;
background : transparent ;
color : var ( -- text ) ;
padding : 0.35 rem 0.75 rem ;
border - radius : calc ( var ( -- radius ) - 2 px ) ;
font : inherit ;
font - size : 0.85 rem ;
cursor : pointer ;
}
. seg button . on {
background : var ( -- accent ) ;
color : # fff ;
}
. asnad - groups {
display : grid ;
gap : 1 rem ;
}
. invoice {
display : grid ;
gap : 0.6 rem ;
border - top : 3 px solid var ( -- accent ) ;
}
. inv - head {
display : grid ;
gap : 0.5 rem ;
padding : 0.6 rem 0.75 rem ;
border - radius : var ( -- radius ) ;
background : color - mix ( in srgb , var ( -- accent ) 10 % , transparent ) ;
border : 1 px solid color - mix ( in srgb , var ( -- accent ) 25 % , transparent ) ;
}
. inv - title strong {
color : var ( -- accent ) ;
}
. inv - title {
display : flex ;
align - items : center ;
gap : 0.5 rem ;
font - size : 0.95 rem ;
}
. inv - badge {
font - size : 0.72 rem ;
padding : 0.1 rem 0.45 rem ;
border - radius : 999 px ;
background : # fef3c7 ;
color : # 92400 e ;
}
. inv - meta {
margin : 0 ;
display : flex ;
flex - wrap : wrap ;
gap : 0.35 rem 1.25 rem ;
}
. inv - meta div {
display : flex ;
gap : 0.35 rem ;
align - items : baseline ;
}
. inv - meta dt {
font - size : 0.76 rem ;
color : var ( -- muted ) ;
}
. inv - meta dd {
margin : 0 ;
font - size : 0.86 rem ;
font - weight : 600 ;
}
. inv - table - wrap {
overflow - x : auto ;
border : 1 px solid var ( -- line ) ;
border - radius : var ( -- radius ) ;
}
. inv - table {
width : 100 % ;
border - collapse : collapse ;
font - size : 0.85 rem ;
}
. inv - table th {
background : var ( -- bg ) ;
color : var ( -- muted ) ;
font - weight : 600 ;
font - size : 0.76 rem ;
text - align : start ;
padding : 0.45 rem 0.55 rem ;
white - space : nowrap ;
border - bottom : 1 px solid var ( -- line ) ;
}
. inv - table td {
padding : 0.45 rem 0.55 rem ;
border - bottom : 1 px solid var ( -- line ) ;
vertical - align : top ;
white - space : nowrap ;
}
. inv - table tbody tr : last - child td {
border - bottom : 0 ;
}
. inv - table tr : has ( + . inv - extra - tr ) td {
border - bottom : 0 ;
}
. inv - num ,
. inv - idx {
font - variant - numeric : tabular - nums ;
}
. inv - idx {
color : var ( -- muted ) ;
width : 2 rem ;
}
. inv - table . inv - desc {
white - space : normal ;
min - width : 180 px ;
}
. inv - desc strong {
display : block ;
}
. inv - desc small {
display : block ;
color : var ( -- muted ) ;
font - size : 0.76 rem ;
}
. inv - extra - tr td {
padding - top : 0 ;
white - space : normal ;
}
. inv - extra - tr summary {
cursor : pointer ;
font - size : 0.76 rem ;
color : var ( -- muted ) ;
margin - bottom : 0.35 rem ;
}
. sg - count {
margin - inline - start : auto ;
font - size : 0.78 rem ;
padding : 0.1 rem 0.5 rem ;
border - radius : 999 px ;
background : var ( -- bg ) ;
border : 1 px solid var ( -- line ) ;
}
. sc - head {
display : flex ;
justify - content : space - between ;
gap : 0.5 rem ;
font - size : 0.85 rem ;
margin - bottom : 0.4 rem ;
margin - bottom : 0.5 rem ;
}
. sc - row {
display : flex ;
@@ -928,6 +1316,63 @@ onMounted(async () => {
. sc - row . cash {
background : color - mix ( in srgb , # 16 a34a 12 % , transparent ) ;
}
. kv {
margin : 0 ;
display : grid ;
grid - template - columns : repeat ( auto - fill , minmax ( 150 px , 1 fr ) ) ;
gap : 0.35 rem ;
}
. kv - item {
min - width : 0 ;
padding : 0.3 rem 0.45 rem ;
border - radius : calc ( var ( -- radius ) - 2 px ) ;
background : var ( -- bg ) ;
}
. kv dt {
font - size : 0.72 rem ;
color : var ( -- muted ) ;
}
. kv dd {
margin : 0 ;
font - size : 0.86 rem ;
font - weight : 600 ;
overflow - wrap : anywhere ;
font - variant - numeric : tabular - nums ;
}
. kv - item . k - Sharh1 ,
. kv - item . k - Sharh2 ,
. kv - item . k - Factor _Code {
grid - column : 1 / - 1 ;
}
. raw - table {
display : grid ;
border : 1 px solid var ( -- line ) ;
border - radius : var ( -- radius ) ;
overflow - x : auto ;
}
. raw - tr {
display : grid ;
grid - template - columns : minmax ( 150 px , 1.4 fr ) repeat ( var ( -- cols ) , minmax ( 110 px , 1 fr ) ) ;
gap : 0.5 rem ;
padding : 0.5 rem 0.7 rem ;
border - top : 1 px solid var ( -- line ) ;
font - size : 0.88 rem ;
align - items : center ;
}
. raw - th {
border - top : 0 ;
background : var ( -- bg ) ;
color : var ( -- muted ) ;
font - size : 0.78 rem ;
font - weight : 700 ;
}
. raw - name {
font - weight : 600 ;
}
. raw - num {
text - align : right ;
font - variant - numeric : tabular - nums ;
}
. sanad - form {
display : grid ;
gap : 0.65 rem ;