@@ -9,10 +9,21 @@ import {
WINERY_SETTLEMENT_RATE ,
} from '@dukang/shared-types' ;
import {
addShanghaiDays ,
calcLogisticsFeeByBottles ,
calcRedeemSettleAmount ,
parseShanghaiYmd ,
pickPayoutsForWithdrawAmount ,
previousShanghaiMonth ,
resolveSettlementRate ,
shanghaiLaggedIssueWindow ,
shanghaiMonthLastInstant ,
shanghaiMonthRange ,
shanghaiPeriodYmds ,
shanghaiT1DayWindow ,
shanghaiYearMonth ,
shanghaiYmd ,
startOfShanghaiDay ,
sumUnbilledPayoutAmount ,
validateStoreWithdraw ,
type LogisticsPricingRule ,
@@ -64,30 +75,8 @@ function paymentProofUrlsInput(urls?: string[]): Prisma.InputJsonValue | typeof
return parsed . length ? ( parsed as Prisma . InputJsonValue ) : Prisma . JsonNull ;
}
/** 上海时区自然日 00:00(用本地 Date 构造;服务器需设 Asia/Shanghai 或等价) */
function startOfDay ( d : Date ) {
return new Date ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) , 0 , 0 , 0 , 0 ) ;
}
/** 核销窗口:昨日 00:00 ≤ t < 今日 00:00;出账日 = 今日 00:00(窗口右端) */
function dayWindow ( anchor = new Date ( ) ) {
const end = startOfDay ( anchor ) ;
const start = new Date ( end ) ;
start . setDate ( start . getDate ( ) - 1 ) ;
return { start , end , billDate : end } ;
}
function shanghaiYmd ( d : Date ) : string {
return d . toLocaleDateString ( 'sv-SE' , { timeZone : 'Asia/Shanghai' } ) ;
}
function wineryDayWindow ( anchor = new Date ( ) , lagDays = WINERY_SETTLEMENT_LAG_DAYS ) {
const billDate = startOfDay ( anchor ) ;
billDate . setDate ( billDate . getDate ( ) - lagDays ) ;
const start = billDate ;
const end = new Date ( start ) ;
end . setDate ( end . getDate ( ) + 1 ) ;
return { start , end , billDate : start } ;
function withShanghaiPeriod < T extends { periodStart : Date ; periodEnd : Date } > ( row : T ) {
return { . . . row , . . . shanghaiPeriodYmds ( row . periodStart ) } ;
}
function toPartnerBillItemDto ( row : {
@@ -180,17 +169,11 @@ function getStoreWithdrawDailyLimit(): number {
/** 工作日 18:00 前未审完视为 FIN-003 超时(Asia/Shanghai 自然日) */
function isWithdrawOverdue ( appliedAt : Date , now = new Date ( ) ) : boolean {
const day = appliedAt . getDay ( ) ; // 0 Sun … 6 Sat
const ymd = shanghaiYmd ( appliedAt ) ;
const noon = new Date ( ` ${ ymd } T12:00:00+08:00 ` ) ;
const day = noon . getUTCDay ( ) ; // 与北京日历日相同
if ( day === 0 || day === 6 ) return false ;
const deadline = new Date (
appliedAt . getFullYear ( ) ,
appliedAt . getMonth ( ) ,
appliedAt . getDate ( ) ,
18 ,
0 ,
0 ,
0 ,
) ;
const deadline = new Date ( ` ${ ymd } T18:00:00+08:00 ` ) ;
return now . getTime ( ) > deadline . getTime ( ) ;
}
@@ -210,15 +193,21 @@ export class SettlementService implements OnModuleInit {
async onModuleInit() {
try {
const shifted = await this . realignStoreBillIssueDates ( ) ;
if ( shifted > 0 ) this . logger . log ( ` Store bill dates aligned to issue day: ${ shifted } ` ) ;
if ( shifted > 0 ) this . logger . log ( ` Store bill dates aligned to Beijing issue day: ${ shifted } ` ) ;
} catch ( e ) {
this . logger . warn ( ` Store bill date align skipped: ${ e instanceof Error ? e.message : e } ` ) ;
}
try {
const shifted = await this . realignWineryBillIssueDates ( ) ;
if ( shifted > 0 ) this . logger . log ( ` Winery bill dates aligned to Beijing issue day: ${ shifted } ` ) ;
} catch ( e ) {
this . logger . warn ( ` Winery bill date align skipped: ${ e instanceof Error ? e.message : e } ` ) ;
}
}
/**
* 旧数据 billDate=核销自然日(窗口左端);现改为出账日(窗口右端=今天 )。
* 按 created_at 日历日对比,只把仍早一天的行 +1;从晚到早更新避免 (storeId,billDate) 冲突 。
* 账单日 = 出账当天(created_at 的北京日历日 00:00+08 )。
* 纠正:旧窗口左端、UTC DATE() +1 误伤、以及 toISOString 跨日 。
*/
private async realignStoreBillIssueDates ( ) : Promise < number > {
const locked = await this . prisma . $queryRaw < Array < { acquired : number | bigint | null } > > `
@@ -226,18 +215,53 @@ export class SettlementService implements OnModuleInit {
` ;
if ( ! Number ( locked [ 0 ] ? . acquired ) ) return 0 ;
try {
const shifted = await this . prisma . $executeRaw `
UPDATE store_bill
SET bill_date = DATE_ADD(bill_date, INTERVAL 1 DAY)
WHERE DATE(bill_date) < DATE(created_at)
ORDER BY bill_date DESC
` ;
return Number ( shifted ) ;
const bills = await this . prisma . storeBill . findMany ( {
select : { id : true , storeId : true , billDate : true , createdAt : true } ,
orderBy : { createdAt : 'desc' } ,
} ) ;
let shifted = 0 ;
for ( const b of bills ) {
const issue = startOfShanghaiDay ( b . createdAt ) ;
if ( shanghaiYmd ( b . billDate ) === shanghaiYmd ( issue ) ) continue ;
const clash = await this . prisma . storeBill . findUnique ( {
where : { storeId_billDate : { storeId : b.storeId , billDate : issue } } ,
} ) ;
if ( clash && clash . id !== b . id ) continue ;
await this . prisma . storeBill . update ( { where : { id : b.id } , data : { billDate : issue } } ) ;
shifted += 1 ;
}
return shifted ;
} finally {
await this . prisma . $queryRaw ` SELECT RELEASE_LOCK('store_bill_issue_date_align') ` ;
}
}
/** 酒厂账单日改为出账当天(不再存 T+3 完成日) */
private async realignWineryBillIssueDates ( ) : Promise < number > {
const locked = await this . prisma . $queryRaw < Array < { acquired : number | bigint | null } > > `
SELECT GET_LOCK('winery_bill_issue_date_align', 5) AS acquired
` ;
if ( ! Number ( locked [ 0 ] ? . acquired ) ) return 0 ;
try {
const bills = await this . prisma . wineryBill . findMany ( {
select : { id : true , billDate : true , createdAt : true } ,
orderBy : { createdAt : 'desc' } ,
} ) ;
let shifted = 0 ;
for ( const b of bills ) {
const issue = startOfShanghaiDay ( b . createdAt ) ;
if ( shanghaiYmd ( b . billDate ) === shanghaiYmd ( issue ) ) continue ;
const clash = await this . prisma . wineryBill . findUnique ( { where : { billDate : issue } } ) ;
if ( clash && clash . id !== b . id ) continue ;
await this . prisma . wineryBill . update ( { where : { id : b.id } , data : { billDate : issue } } ) ;
shifted += 1 ;
}
return shifted ;
} finally {
await this . prisma . $queryRaw ` SELECT RELEASE_LOCK('winery_bill_issue_date_align') ` ;
}
}
private notifyPartnerBillDigest (
period : string ,
rows : Array < {
@@ -424,9 +448,8 @@ export class SettlementService implements OnModuleInit {
}
private async todayWithdrawAppliedAmount ( storeId : bigint , now = new Date ( ) ) {
const start = startOfDay ( now ) ;
const end = new Date ( start ) ;
end . setDate ( end . getDate ( ) + 1 ) ;
const start = startOfShanghaiDay ( now ) ;
const end = addShanghaiDays ( start , 1 ) ;
const agg = await this . prisma . storeWithdrawRequest . aggregate ( {
where : {
storeId ,
@@ -897,7 +920,7 @@ export class SettlementService implements OnModuleInit {
. . . lines ,
'请尽快在 HQ「财务 → 门店账单(手动提现)」处理。' ,
] . join ( '\n' ) ,
dedupeKey : ` store_withdraw_overdue| ${ new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) } ` ,
dedupeKey : ` store_withdraw_overdue| ${ shanghaiYmd ( new Date ( ) ) } ` ,
dedupeTtlSec : 6 * 3600 ,
} ) ;
}
@@ -1075,9 +1098,9 @@ export class SettlementService implements OnModuleInit {
// ─── Store bills (daily header) ──────────────────────
/** 生成昨日核销窗口的门店对账单;billDate = 出账日(今日 ) */
/** 生成昨日核销窗口的门店对账单;billDate = 出账日(北京时间今天 ) */
async generateStoreBillsForDay ( anchor = new Date ( ) ) {
const { start , end , billDate } = dayWindow ( anchor ) ;
const { start , end , billDate } = shanghaiT1DayWindow ( anchor ) ;
const payouts = await this . prisma . storePayout . findMany ( {
where : {
storeBillId : null ,
@@ -1275,12 +1298,8 @@ export class SettlementService implements OnModuleInit {
if ( query . storeId ) where . storeId = BigInt ( query . storeId ) ;
if ( query . dateFrom || query . dateTo ) {
where . appliedAt = { } ;
if ( query . dateFrom ) where . appliedAt . gte = new Date ( query . dateFrom ) ;
if ( query . dateTo ) {
const end = new Date ( query . dateTo ) ;
end . setHours ( 23 , 59 , 59 , 999 ) ;
where . appliedAt . lte = end ;
}
if ( query . dateFrom ) where . appliedAt . gte = parseShanghaiYmd ( query . dateFrom ) ;
if ( query . dateTo ) where . appliedAt . lt = addShanghaiDays ( parseShanghaiYmd ( query . dateTo ) , 1 ) ;
}
return where ;
} ) ( )
@@ -1564,8 +1583,8 @@ export class SettlementService implements OnModuleInit {
if ( query . storeId ) where . storeId = BigInt ( query . storeId ) ;
if ( query . dateFrom || query . dateTo ) {
where . billDate = { } ;
if ( query . dateFrom ) where . billDate . gte = startOfDay ( new Date ( query . dateFrom ) ) ;
if ( query . dateTo ) where . billDate . lte = startOfDay ( new Date ( query . dateTo ) ) ;
if ( query . dateFrom ) where . billDate . gte = parseShanghaiYmd ( query . dateFrom ) ;
if ( query . dateTo ) where . billDate . lte = parseShanghaiYmd ( query . dateTo ) ;
}
return where ;
}
@@ -1598,7 +1617,7 @@ export class SettlementService implements OnModuleInit {
eventName : 'partner_bill_view' ,
extraJson : { count : bills.length } ,
} ) ;
return serializeBigInt ( bills ) ;
return serializeBigInt ( bills . map ( withShanghaiPeriod ) ) ;
}
async getPartnerBill ( partnerAccountId : bigint , billId : bigint ) {
@@ -1617,7 +1636,7 @@ export class SettlementService implements OnModuleInit {
} ) ;
const { items , . . . header } = bill ;
return serializeBigInt ( {
. . . header ,
. . . withShanghaiPeriod ( header ) ,
partnerId : header.partnerAccountId.toString ( ) ,
. . . splitPartnerBillItems ( items ) ,
} ) ;
@@ -1664,7 +1683,7 @@ export class SettlementService implements OnModuleInit {
] ) ;
const items = rawItems . map ( ( b ) = > ( {
. . . b ,
. . . withShanghaiPeriod ( b ) ,
partner : b.partnerAccount ,
} ) ) ;
@@ -1688,9 +1707,8 @@ export class SettlementService implements OnModuleInit {
if ( query . status ) where . status = query . status as Prisma . EnumPartnerBillStatusFilter [ 'equals' ] ;
if ( query . partnerId ) where . partnerAccountId = BigInt ( query . partnerId ) ;
if ( query . year && query . month ) {
const periodStart = new Date ( query . year , query . month - 1 , 1 ) ;
const periodEnd = new Date ( query . year , query . month , 0 , 23 , 59 , 59 , 999 ) ;
where . periodStart = { gte : periodStart , lte : periodEnd } ;
const { start , endExclusive } = shanghaiMonthRange ( query . year , query . month ) ;
where . periodStart = { gte : start , lt : endExclusive } ;
}
return where ;
}
@@ -1767,8 +1785,8 @@ export class SettlementService implements OnModuleInit {
/** 每月 1 日任务:生成上一自然月账单 */
async generatePreviousMonthPartnerBills ( anchor = new Date ( ) ) {
const prev = new Date ( anchor . getFullYear ( ) , anchor . getMonth ( ) - 1 , 1 ) ;
return this . generateAllPartnerBills ( { year : prev.getFullYear ( ) , month : prev.getMonth ( ) + 1 } ) ;
const prev = previousShanghaiMonth ( anchor ) ;
return this . generateAllPartnerBills ( { year : prev.year , month : prev.month } ) ;
}
async getAdminPartnerBill ( id : bigint ) {
@@ -1779,7 +1797,7 @@ export class SettlementService implements OnModuleInit {
if ( ! bill ) throw new NotFoundException ( '账单不存在' ) ;
const { items , . . . header } = bill ;
return serializeBigInt ( {
. . . header ,
. . . withShanghaiPeriod ( header ) ,
partnerId : header.partnerAccountId.toString ( ) ,
. . . splitPartnerBillItems ( items ) ,
} ) ;
@@ -1791,12 +1809,13 @@ export class SettlementService implements OnModuleInit {
) {
const partnerAccountId = BigInt ( body . partnerId ) ;
const primary = await this . partnerCityService . resolvePrimaryAccount ( partnerAccountId ) ;
const periodStart = new Date ( body . year , body . month - 1 , 1 ) ;
const periodEnd = new Date ( body . year , body . month , 0 , 23 , 59 , 59 , 999 ) ;
const { start : periodStart , endExclusive } = shanghaiMonthRange ( body . year , body . month ) ;
const periodEnd = shanghaiMonthLastInstant ( body . year , body . month ) ;
const existing = await this . prisma . partnerBill . findUnique ( {
const existing = await this . prisma . partnerBill . findFirst ( {
where : {
partnerAccountId_periodStart : { partnerAccountId : primary.id , periodStart } ,
partnerAccountId : primary.id ,
periodStart : { gte : periodStart , lt : endExclusive } ,
} ,
} ) ;
if ( existing && existing . status !== 'PENDING_REVIEW' ) {
@@ -1876,6 +1895,7 @@ export class SettlementService implements OnModuleInit {
orderCommission : round2 ( orderCommission ) ,
redeemCommission : round2 ( redeemCommission ) ,
totalAmount ,
periodStart ,
periodEnd ,
status : 'PENDING_REVIEW' ,
} ,
@@ -2142,15 +2162,15 @@ export class SettlementService implements OnModuleInit {
csvEscape ( b . billNo ) ,
csvEscape ( b . partnerAccount . companyName ? ? '' ) ,
csvEscape ( b . partnerAccount . phone ? ? '' ) ,
b . periodStart . toISOString ( ) . slice ( 0 , 10 ) ,
b . periodEnd . toISOString ( ) . slice ( 0 , 10 ) ,
shanghaiPeriodYmds ( b . periodStart ) . periodStart ,
shanghaiPeriodYmds ( b . periodStart ) . periodEnd ,
Number ( b . orderCommission ) ,
Number ( b . redeemCommission ) ,
Number ( b . totalAmount ) ,
b . status ,
b . sentAt ? b . sentAt . toISOString ( ) . slice ( 0 , 10 ) : '' ,
b . confirmedAt ? b . confirmedAt . toISOString ( ) . slice ( 0 , 10 ) : '' ,
b . paidAt ? b . paidAt . toISOString ( ) . slice ( 0 , 10 ) : '' ,
b . sentAt ? shanghaiYmd ( b . sentAt ) : '' ,
b . confirmedAt ? shanghaiYmd ( b . confirmedAt ) : '' ,
b . paidAt ? shanghaiYmd ( b . paidAt ) : '' ,
csvEscape ( b . paymentRef ? ? '' ) ,
csvEscape ( b . partnerAccount . bankAccountName ? ? '' ) ,
csvEscape ( b . partnerAccount . bankAccountNo ? ? '' ) ,
@@ -2182,15 +2202,20 @@ export class SettlementService implements OnModuleInit {
// ─── Winery bills ────────────────────────────────────
async generateWineryBillForDay ( anchor = new Date ( ) ) {
const { start , end , billDate } = wineryDayWindow ( anchor ) ;
const { start , end , billDate } = shanghaiLaggedIssueWindow ( anchor , WINERY_SETTLEMENT_LAG_DAYS ) ;
const rate = WINERY_SETTLEMENT_RATE ;
const issueYmd = shanghaiYmd ( billDate ) ;
const existing = await this . prisma . wineryBill . findUnique ( { where : { billDate } } ) ;
const existing =
( await this . prisma . wineryBill . findUnique ( { where : { billDate } } ) ) ? ?
( await this . prisma . wineryBill . findFirst ( {
where : { createdAt : { gte : billDate , lt : addShanghaiDays ( billDate , 1 ) } } ,
} ) ) ;
if ( existing ? . status === 'PAID' ) {
return { billDate : billDate.toISOString ( ) . slice ( 0 , 10 ) , skipped : true , reason : '已打款' } ;
return { billDate : issueYmd , skipped : true , reason : '已打款' } ;
}
// T+3:纳入「账单日 」当天完成的同城/跨城订单(完成日 = 今天 − lagDays )
// T+3:纳入「今天 − lagDays 」当天完成的同城/跨城订单; billDate = 出账日(今天)
const orders = await this . prisma . order . findMany ( {
where : {
status : 'COMPLETED' ,
@@ -2203,7 +2228,7 @@ export class SettlementService implements OnModuleInit {
} ) ;
if ( orders . length === 0 && ! existing ) {
return { billDate : billDate.toISOString ( ) . slice ( 0 , 10 ) , skipped : true , reason : '无订单' } ;
return { billDate : issueYmd , skipped : true , reason : '无订单' } ;
}
const orderAmount = round2 ( orders . reduce ( ( s , o ) = > s + Number ( o . payAmount ) , 0 ) ) ;
@@ -2214,6 +2239,7 @@ export class SettlementService implements OnModuleInit {
? await tx . wineryBill . update ( {
where : { id : existing.id } ,
data : {
billDate ,
orderCount : orders.length ,
orderAmount ,
wineryRate : rate ,
@@ -2254,7 +2280,7 @@ export class SettlementService implements OnModuleInit {
return header ;
} ) ;
const period = billDate . toISOString ( ) . slice ( 0 , 10 ) ;
const period = issueYmd ;
const wineryBank = await loadWineryBankConfig ( this . prisma ) ;
const bankParts = wecomBankParts ( wineryBank ) ;
const cityNames = joinLimited (
@@ -2324,7 +2350,13 @@ export class SettlementService implements OnModuleInit {
totalAmount : Number ( aggregates . _sum . wineryAmount ? ? 0 ) ,
} ;
return serializeBigInt ( { items , total , page , pageSize , summary } ) ;
return serializeBigInt ( {
items : items.map ( ( b ) = > ( { . . . b , billDate : shanghaiYmd ( b . billDate ) } ) ) ,
total ,
page ,
pageSize ,
summary ,
} ) ;
}
async getAdminWineryBill ( id : bigint ) {
@@ -2334,7 +2366,7 @@ export class SettlementService implements OnModuleInit {
} ) ;
if ( ! bill ) throw new NotFoundException ( '酒厂对账单不存在' ) ;
const wineryBank = await loadWineryBankConfig ( this . prisma ) ;
return serializeBigInt ( { . . . bill , wineryBank } ) ;
return serializeBigInt ( { . . . bill , billDate : shanghaiYmd ( bill . billDate ) , wineryBank } ) ;
}
async confirmWineryBill ( id : bigint , dto : { paymentRef? : string } = { } ) {
@@ -2418,7 +2450,7 @@ export class SettlementService implements OnModuleInit {
rows . push (
[
csvEscape ( b . billNo ) ,
b . billDate . toISOString ( ) . slice ( 0 , 10 ) ,
shanghaiYmd ( b . billDate ) ,
'' ,
'' ,
Number ( b . orderAmount ) ,
@@ -2435,7 +2467,7 @@ export class SettlementService implements OnModuleInit {
rows . push (
[
csvEscape ( b . billNo ) ,
b . billDate . toISOString ( ) . slice ( 0 , 10 ) ,
shanghaiYmd ( b . billDate ) ,
csvEscape ( item . orderNo ) ,
item . deliveryType === 'LOCAL' ? '同城' : item . deliveryType === 'CROSS_CITY' ? '跨城' : item . deliveryType ,
Number ( item . payAmount ) ,
@@ -2469,13 +2501,12 @@ export class SettlementService implements OnModuleInit {
where . wineryAmount = { gt : 0 } ;
}
if ( query . year && query . month ) {
const start = new Date ( query . year , query . month - 1 , 1 ) ;
const end = new Date ( query . year , query . month , 0 , 23 , 59 , 59 , 999 ) ;
where . billDate = { gte : start , lte : end } ;
const { start , endExclusive } = shanghaiMonthRange ( query . year , query . month ) ;
where . billDate = { gte : start , lt : endExclusive } ;
} else if ( query . dateFrom || query . dateTo ) {
where . billDate = { } ;
if ( query . dateFrom ) where . billDate . gte = startOfDay ( new Date ( query . dateFrom ) ) ;
if ( query . dateTo ) where . billDate . lte = startOfDay ( new Date ( query . dateTo ) ) ;
if ( query . dateFrom ) where . billDate . gte = parseShanghaiYmd ( query . dateFrom ) ;
if ( query . dateTo ) where . billDate . lte = parseShanghaiYmd ( query . dateTo ) ;
}
return where ;
}
@@ -2483,10 +2514,10 @@ export class SettlementService implements OnModuleInit {
// ─── Logistics bills (按承运商月结) ───────────────────
async generatePreviousMonthLogisticsBills ( anchor = new Date ( ) ) {
const prev = new Date ( anchor . getFullYear ( ) , anchor . getMonth ( ) - 1 , 1 ) ;
const prev = previousShanghaiMonth ( anchor ) ;
return this . generateAllLogisticsBills ( {
year : prev.getFullYear ( ) ,
month : prev.getMonth ( ) + 1 ,
year : prev.year ,
month : prev.month ,
} ) ;
}
@@ -2590,12 +2621,13 @@ export class SettlementService implements OnModuleInit {
} ) ;
if ( ! provider ) throw new NotFoundException ( '仓配承运商不存在' ) ;
const periodStart = new Date ( body . year , body . month - 1 , 1 ) ;
const periodEnd = new Date ( body . year , body . month , 0 , 23 , 59 , 59 , 999 ) ;
const { start : periodStart , endExclusive } = shanghaiMonthRange ( body . year , body . month ) ;
const periodEnd = shanghaiMonthLastInstant ( body . year , body . month ) ;
const existing = await this . prisma . logisticsBill . findUnique ( {
const existing = await this . prisma . logisticsBill . findFirst ( {
where : {
fulfillmentProviderId_periodStart : { fulfillmentProviderId , periodStart } ,
fulfillmentProviderId ,
periodStart : { gte : periodStart , lt : endExclusive } ,
} ,
} ) ;
if ( existing ? . status === 'PAID' ) {
@@ -2676,6 +2708,7 @@ export class SettlementService implements OnModuleInit {
? await tx . logisticsBill . update ( {
where : { id : existing.id } ,
data : {
periodStart ,
periodEnd ,
orderCount ,
bottleCount ,
@@ -2814,7 +2847,7 @@ export class SettlementService implements OnModuleInit {
] ) ;
const items = rawItems . map ( ( b ) = > ( {
. . . b ,
. . . withShanghaiPeriod ( b ) ,
providerCode : b.fulfillmentProvider.code ,
providerName : b.fulfillmentProvider.name ,
pricingSnapshot : this.fulfillmentProviderService.parsePricingRules ( b . pricingSnapshotJson ) ,
@@ -2831,10 +2864,11 @@ export class SettlementService implements OnModuleInit {
}
async listLogisticsProviderSummary ( query : { year? : number ; month? : number } ) {
const year = query . year ? ? new Date ( ) . getFullYear ( ) ;
const month = query . month ? ? new Date ( ) . getMonth ( ) + 1 ;
const periodStart = new Date ( year , month - 1 , 1 ) ;
const periodEnd = new Date ( year , month , 0 , 23 , 59 , 59 , 999 ) ;
const fallback = shanghaiYearMonth ( ) ;
const year = query . year ? ? fallback . year ;
const month = query . month ? ? fallback . month ;
const { start : periodStart , endExclusive } = shanghaiMonthRange ( year , month ) ;
const periodEnd = new Date ( endExclusive . getTime ( ) - 1 ) ;
const providers = await this . prisma . fulfillmentProvider . findMany ( {
orderBy : { code : 'asc' } ,
@@ -2897,12 +2931,10 @@ export class SettlementService implements OnModuleInit {
) ;
}
const bill = await this . prisma . logisticsBill . findUnique ( {
const bill = await this . prisma . logisticsBill . findFirst ( {
where : {
fulfillmentProviderId_periodStart : {
fulfillmentProviderId : p.id ,
periodStart ,
} ,
fulfillmentProviderId : p.id ,
periodStart : { gte : periodStart , lt : endExclusive } ,
} ,
} ) ;
@@ -2938,7 +2970,7 @@ export class SettlementService implements OnModuleInit {
} ) ;
if ( ! bill ) throw new NotFoundException ( '物流对账单不存在' ) ;
return serializeBigInt ( {
. . . bill ,
. . . withShanghaiPeriod ( bill ) ,
providerCode : bill.fulfillmentProvider.code ,
providerName : bill.fulfillmentProvider.name ,
pricingSnapshot : this.fulfillmentProviderService.parsePricingRules ( bill . pricingSnapshotJson ) ,
@@ -3056,8 +3088,8 @@ export class SettlementService implements OnModuleInit {
csvEscape ( b . billNo ) ,
csvEscape ( p . code ) ,
csvEscape ( p . name ) ,
b . periodStart . toISOString ( ) . slice ( 0 , 10 ) ,
b . periodEnd . toISOString ( ) . slice ( 0 , 10 ) ,
shanghaiPeriodYmds ( b . periodStart ) . periodStart ,
shanghaiPeriodYmds ( b . periodStart ) . periodEnd ,
b . settlementMethod ,
'' ,
b . bottleCount ,
@@ -3075,8 +3107,8 @@ export class SettlementService implements OnModuleInit {
csvEscape ( b . billNo ) ,
csvEscape ( p . code ) ,
csvEscape ( p . name ) ,
b . periodStart . toISOString ( ) . slice ( 0 , 10 ) ,
b . periodEnd . toISOString ( ) . slice ( 0 , 10 ) ,
shanghaiPeriodYmds ( b . periodStart ) . periodStart ,
shanghaiPeriodYmds ( b . periodStart ) . periodEnd ,
b . settlementMethod ,
csvEscape ( item . orderNo ) ,
item . quantity ,
@@ -3101,9 +3133,8 @@ export class SettlementService implements OnModuleInit {
if ( query . status === 'UNPAID' || query . status === 'PAID' ) where . status = query . status ;
if ( query . providerId ) where . fulfillmentProviderId = BigInt ( query . providerId ) ;
if ( query . year && query . month ) {
const periodStart = new Date ( query . year , query . month - 1 , 1 ) ;
const periodEnd = new Date ( query . year , query . month , 0 , 23 , 59 , 59 , 999 ) ;
where . periodStart = { gte : periodStart , lte : periodEnd } ;
const { start , endExclusive } = shanghaiMonthRange ( query . year , query . month ) ;
where . periodStart = { gte : start , lt : endExclusive } ;
}
return where ;
}