@@ -79,6 +79,7 @@ export async function createCompany(values: z.infer<typeof createCompanySchema>)
7979 companyId : newCompany . id ,
8080 userId : session . id ,
8181 quantity : '1000.00000000' ,
82+ avgCost : '1.00' ,
8283 } ) ;
8384
8485 return { success : `L'entreprise "${ name } " a été créée avec succès ! ${ creationCost . toLocaleString ( ) } $ ont été transférés à la trésorerie.` } ;
@@ -109,10 +110,16 @@ export async function getCompaniesForUserDashboard() {
109110 orderBy : ( companies , { desc } ) => [ desc ( companies . createdAt ) ] ,
110111 } ) ;
111112
112- const companiesWithMarketData = allCompanies . map ( company => {
113- const cash = parseFloat ( company . cash ) ;
113+ const companiesWithMarketData = await Promise . all ( allCompanies . map ( async ( company ) => {
114+ const nav = await getCompanyNAV ( company . id , db ) ;
114115 const totalShares = parseFloat ( company . totalShares ) ;
115- const sharePrice = parseFloat ( company . sharePrice ) ;
116+ const sharePrice = totalShares > 0 ? nav / totalShares : 0 ;
117+
118+ // Update share price in DB if it has changed
119+ if ( Math . abs ( sharePrice - parseFloat ( company . sharePrice ) ) > 1e-9 ) {
120+ await db . update ( companies ) . set ( { sharePrice : sharePrice . toString ( ) } ) . where ( eq ( companies . id , company . id ) ) ;
121+ }
122+
116123 const marketCap = totalShares * sharePrice ;
117124
118125 const historicalData : CompanyHistoricalPoint [ ] = [ ] ;
@@ -137,14 +144,15 @@ export async function getCompaniesForUserDashboard() {
137144
138145 return {
139146 ...company ,
140- cash : cash ,
147+ cash : parseFloat ( company . cash ) ,
141148 marketCap : marketCap ,
142149 sharePrice : sharePrice ,
143150 totalShares : totalShares ,
144151 historicalData,
145152 change24h,
146153 }
147- } ) ;
154+ } ) ) ;
155+
148156
149157 if ( ! session ?. id ) {
150158 return {
@@ -171,19 +179,16 @@ export async function getCompaniesForUserDashboard() {
171179 const shareData = sharesByCompanyId . get ( company . id ) ;
172180 const sharesHeld = parseFloat ( shareData ?. quantity || '0' ) ;
173181
174- const isManaged = ! ! membership ;
175- const isInvested = sharesHeld > 0 ;
176-
177182 const companyData = {
178183 ...company ,
179184 sharesHeld : sharesHeld ,
180185 sharesValue : sharesHeld * company . sharePrice ,
181186 role : membership ?. role ,
182187 } ;
183188
184- if ( isManaged ) {
189+ if ( membership ) {
185190 managedCompanies . push ( companyData ) ;
186- } else if ( isInvested ) {
191+ } else if ( sharesHeld > 0 ) {
187192 investedCompanies . push ( companyData ) ;
188193 } else {
189194 otherCompanies . push ( companyData ) ;
@@ -265,9 +270,11 @@ export async function getCompanyById(companyId: number) {
265270 }
266271 }
267272
268- // Calculate market cap based on the stored share price, not NAV
269- const sharePrice = parseFloat ( company . sharePrice ) ;
273+ // Recalculate share price based on NAV
274+ const nav = await getCompanyNAV ( companyId , db ) ;
270275 const totalShares = parseFloat ( company . totalShares ) ;
276+ const sharePrice = totalShares > 0 ? nav / totalShares : 0 ;
277+
271278 const marketCap = sharePrice * totalShares ;
272279
273280 const miningRigsValue = company . miningRigs . reduce ( ( total , ownedRig ) => {
@@ -301,7 +308,7 @@ export async function getCompanyById(companyId: number) {
301308export type CompanyWithDetails = NonNullable < Awaited < ReturnType < typeof getCompanyById > > > ;
302309
303310// Universal function to calculate a company's Net Asset Value (NAV)
304- async function getCompanyNAV ( companyId : number , tx : any ) {
311+ export async function getCompanyNAV ( companyId : number , tx : any ) {
305312 const company = await tx . query . companies . findFirst ( {
306313 where : eq ( companies . id , companyId ) ,
307314 with : { holdings : true , miningRigs : true }
@@ -373,16 +380,6 @@ export async function buyAssetForCompany(companyId: number, ticker: string, quan
373380 avgCost : asset . price ,
374381 } ) ;
375382 }
376-
377- // Recalculate NAV and new share price because asset values can change
378- const companyData = await tx . query . companies . findFirst ( { where : eq ( companies . id , companyId ) } ) ;
379- const nav = await getCompanyNAV ( companyId , tx ) ;
380- const totalShares = parseFloat ( companyData ! . totalShares ) ;
381- if ( totalShares > 0 ) {
382- const newSharePrice = nav / totalShares ;
383- await tx . update ( companies ) . set ( { sharePrice : newSharePrice . toString ( ) } ) . where ( eq ( companies . id , companyId ) ) ;
384- }
385-
386383 return { success : `L'entreprise a acheté ${ quantity } de ${ ticker } .` } ;
387384 } ) ;
388385
@@ -423,15 +420,6 @@ export async function sellAssetForCompany(companyId: number, holdingId: number,
423420 } else {
424421 await tx . delete ( companyHoldings ) . where ( eq ( companyHoldings . id , holdingId ) ) ;
425422 }
426-
427- const companyData = await tx . query . companies . findFirst ( { where : eq ( companies . id , companyId ) } ) ;
428- const nav = await getCompanyNAV ( companyId , tx ) ;
429- const totalShares = parseFloat ( companyData ! . totalShares ) ;
430- if ( totalShares > 0 ) {
431- const newSharePrice = nav / totalShares ;
432- await tx . update ( companies ) . set ( { sharePrice : newSharePrice . toString ( ) } ) . where ( eq ( companies . id , companyId ) ) ;
433- }
434-
435423 return { success : `L'entreprise a vendu ${ quantity } de ${ asset . ticker } .` } ;
436424 } ) ;
437425
@@ -473,14 +461,6 @@ export async function buyMiningRigForCompany(companyId: number, rigId: string):
473461 await tx . insert ( companyMiningRigs ) . values ( { companyId, rigId, quantity : 1 } ) ;
474462 }
475463
476- const companyData = await tx . query . companies . findFirst ( { where : eq ( companies . id , companyId ) } ) ;
477- const newNav = await getCompanyNAV ( companyId , tx ) ;
478- const totalShares = parseFloat ( companyData ! . totalShares ) ;
479- if ( totalShares > 0 ) {
480- const newSharePrice = newNav / totalShares ;
481- await tx . update ( companies ) . set ( { sharePrice : newSharePrice . toString ( ) } ) . where ( eq ( companies . id , companyId ) ) ;
482- }
483-
484464 return { success : `L'entreprise a acheté un ${ rigToBuy . name } .` } ;
485465 } ) ;
486466
@@ -508,7 +488,11 @@ export async function investInCompany(companyId: number, amount: number): Promis
508488 const companyData = await tx . query . companies . findFirst ( { where : eq ( companies . id , companyId ) } ) ;
509489 if ( ! companyData ) throw new Error ( "Entreprise non trouvée." ) ;
510490
511- const sharePrice = parseFloat ( companyData . sharePrice ) ;
491+ // Recalculate share price based on NAV before transaction
492+ const currentNav = await getCompanyNAV ( companyId , tx ) ;
493+ const currentTotalShares = parseFloat ( companyData . totalShares ) ;
494+ const sharePrice = currentTotalShares > 0 ? currentNav / currentTotalShares : 1 ; // Fallback to 1 if no shares
495+
512496 if ( sharePrice <= 0 ) throw new Error ( "Prix de l'action non valide, impossible d'investir." ) ;
513497
514498 const sharesToBuy = amount / sharePrice ;
@@ -520,10 +504,22 @@ export async function investInCompany(companyId: number, amount: number): Promis
520504 } ) ;
521505
522506 if ( existingShares ) {
523- const newQuantity = parseFloat ( existingShares . quantity ) + sharesToBuy ;
524- await tx . update ( companyShares ) . set ( { quantity : newQuantity . toString ( ) } ) . where ( eq ( companyShares . id , existingShares . id ) ) ;
507+ const oldQuantity = parseFloat ( existingShares . quantity ) ;
508+ const oldAvgCost = parseFloat ( existingShares . avgCost ) ;
509+ const newTotalQuantity = oldQuantity + sharesToBuy ;
510+ const newAvgCost = ( ( oldAvgCost * oldQuantity ) + amount ) / newTotalQuantity ;
511+
512+ await tx . update ( companyShares ) . set ( {
513+ quantity : newTotalQuantity . toString ( ) ,
514+ avgCost : newAvgCost . toString ( ) ,
515+ } ) . where ( eq ( companyShares . id , existingShares . id ) ) ;
525516 } else {
526- await tx . insert ( companyShares ) . values ( { userId : session . id , companyId : companyId , quantity : sharesToBuy . toString ( ) } ) ;
517+ await tx . insert ( companyShares ) . values ( {
518+ userId : session . id ,
519+ companyId : companyId ,
520+ quantity : sharesToBuy . toString ( ) ,
521+ avgCost : sharePrice . toString ( ) ,
522+ } ) ;
527523 }
528524
529525 // Update company state
@@ -534,11 +530,6 @@ export async function investInCompany(companyId: number, amount: number): Promis
534530 cash : newCompanyCash . toString ( ) ,
535531 totalShares : newTotalShares . toString ( ) ,
536532 } ) . where ( eq ( companies . id , companyId ) ) ;
537-
538- // Recalculate NAV and new share price
539- const newNav = await getCompanyNAV ( companyId , tx ) ;
540- const newSharePrice = newNav / newTotalShares ;
541- await tx . update ( companies ) . set ( { sharePrice : newSharePrice . toString ( ) } ) . where ( eq ( companies . id , companyId ) ) ;
542533
543534 await tx . insert ( transactions ) . values ( {
544535 userId : session . id ,
@@ -575,15 +566,17 @@ export async function sellShares(companyId: number, quantity: number): Promise<{
575566 const company = await tx . query . companies . findFirst ( { where : eq ( companies . id , companyId ) } ) ;
576567 if ( ! company ) throw new Error ( "Entreprise non trouvée." ) ;
577568
578- const sharePrice = parseFloat ( company . sharePrice ) ;
579- const proceeds = sharePrice * quantity ;
580-
581569 const userShareHolding = await tx . query . companyShares . findFirst ( {
582570 where : and ( eq ( companyShares . userId , session . id ) , eq ( companyShares . companyId , companyId ) )
583571 } ) ;
584572 const sharesHeld = parseFloat ( userShareHolding ?. quantity || '0' ) ;
585573 if ( sharesHeld < quantity ) throw new Error ( "Vous ne possédez pas assez de parts." ) ;
586-
574+
575+ const currentNav = await getCompanyNAV ( companyId , tx ) ;
576+ const currentTotalShares = parseFloat ( company . totalShares ) ;
577+ const sharePrice = currentTotalShares > 0 ? currentNav / currentTotalShares : 0 ;
578+ const proceeds = sharePrice * quantity ;
579+
587580 const companyCash = parseFloat ( company . cash ) ;
588581 if ( companyCash < proceeds ) throw new Error ( "La trésorerie de l'entreprise est insuffisante pour racheter ces parts." ) ;
589582
@@ -607,11 +600,6 @@ export async function sellShares(companyId: number, quantity: number): Promise<{
607600 totalShares : newTotalShares . toString ( ) ,
608601 } ) . where ( eq ( companies . id , companyId ) ) ;
609602
610- // Recalculate NAV and new share price
611- const newNav = await getCompanyNAV ( companyId , tx ) ;
612- const newSharePrice = newTotalShares > 0 ? newNav / newTotalShares : 0 ;
613- await tx . update ( companies ) . set ( { sharePrice : newSharePrice . toString ( ) } ) . where ( eq ( companies . id , companyId ) ) ;
614-
615603 await tx . insert ( transactions ) . values ( {
616604 userId : session . id ,
617605 type : 'Sell' ,
@@ -655,15 +643,6 @@ export async function addCashToCompany(companyId: number, amount: number): Promi
655643 await tx . update ( users ) . set ( { cash : ( parseFloat ( user . cash ) - amount ) . toString ( ) } ) . where ( eq ( users . id , session . id ) ) ;
656644 await tx . update ( companies ) . set ( { cash : sql `${ companies . cash } + ${ amount } ` } ) . where ( eq ( companies . id , companyId ) ) ;
657645
658- // Recalculate NAV and new share price
659- const company = await tx . query . companies . findFirst ( { where : eq ( companies . id , companyId ) } ) ;
660- const newNav = await getCompanyNAV ( companyId , tx ) ;
661- const totalShares = parseFloat ( company ! . totalShares ) ;
662- if ( totalShares > 0 ) {
663- const newSharePrice = newNav / totalShares ;
664- await tx . update ( companies ) . set ( { sharePrice : newSharePrice . toString ( ) } ) . where ( eq ( companies . id , companyId ) ) ;
665- }
666-
667646 return { success : `${ amount . toFixed ( 2 ) } $ ajoutés à la trésorerie de l'entreprise.` } ;
668647 } ) ;
669648
@@ -697,16 +676,7 @@ export async function withdrawFromCompanyTreasury(companyId: number, amount: num
697676
698677 await tx . update ( companies ) . set ( { cash : sql `${ companies . cash } - ${ amount } ` } ) . where ( eq ( companies . id , companyId ) ) ;
699678 await tx . update ( users ) . set ( { cash : ( parseFloat ( user . cash ) + amount ) . toString ( ) } ) . where ( eq ( users . id , session . id ) ) ;
700-
701- // Recalculate NAV and new share price
702- const fullCompany = await tx . query . companies . findFirst ( { where : eq ( companies . id , companyId ) } ) ;
703- const newNav = await getCompanyNAV ( companyId , tx ) ;
704- const totalShares = parseFloat ( fullCompany ! . totalShares ) ;
705- if ( totalShares > 0 ) {
706- const newSharePrice = newNav / totalShares ;
707- await tx . update ( companies ) . set ( { sharePrice : newSharePrice . toString ( ) } ) . where ( eq ( companies . id , companyId ) ) ;
708- }
709-
679+
710680 return { success : `${ amount . toFixed ( 2 ) } $ retirés de la trésorerie de l'entreprise.` } ;
711681 } ) ;
712682
@@ -815,22 +785,15 @@ export async function listCompanyOnMarket(companyId: number): Promise<{ success?
815785 if ( ! company ) throw new Error ( "Entreprise non trouvée." ) ;
816786 if ( company . isListed ) throw new Error ( "L'entreprise est déjà cotée." ) ;
817787
818- // Recalculate NAV and share price one last time before listing
819- const nav = await getCompanyNAV ( companyId , tx ) ;
820- const totalShares = parseFloat ( company . totalShares ) ;
821- const sharePrice = totalShares > 0 ? nav / totalShares : 0 ;
822-
823788 await tx . update ( companies ) . set ( {
824789 isListed : true ,
825- sharePrice : sharePrice . toString ( )
826790 } ) . where ( eq ( companies . id , companyId ) ) ;
827791
828792 return { success : 'Entreprise mise en bourse avec succès !' } ;
829793 } ) ;
830794
831- revalidatePath ( '/trading' ) ;
795+ revalidatePath ( `/companies` ) ;
832796 revalidatePath ( `/companies/${ companyId } ` ) ;
833- revalidatePath ( '/companies' ) ;
834797
835798 return result ;
836799 } catch ( error : any ) {
0 commit comments