@@ -4,6 +4,8 @@ import { syncJournalEntryToAirtable } from "./airtableJournals.js";
44import { deleteProjectSubmissionsFromYsws , ensureYswsProjectSubmissionsTable , submitProjectToYsws } from "./airtableYsws.js" ;
55import { fetchHackatimeProjects , sumHackatimeHoursForNames } from "./hackatimeAuth.js" ;
66
7+ export const BRICKS_PER_APPROVED_HOUR = 20 ;
8+
79export async function ensureProjectsTable ( ) {
810 if ( ! pool ) {
911 console . warn ( "[projects] DATABASE_URL not set; skipping projects table setup." ) ;
@@ -97,6 +99,35 @@ export async function ensureProjectsTable() {
9799 await pool . query ( `UPDATE projects SET fraud_flag = FALSE WHERE fraud_flag IS NULL` ) ;
98100 await pool . query ( `UPDATE projects SET ship_kind = 'initial' WHERE ship_kind IS NULL` ) ;
99101 await pool . query ( `UPDATE projects SET blocked = FALSE WHERE blocked IS NULL` ) ;
102+ await pool . query ( `
103+ WITH mismatched AS (
104+ SELECT
105+ id,
106+ user_id,
107+ COALESCE(bricks_earned, 0) AS old_bricks,
108+ ROUND(COALESCE(approved_hours, 0) * $1, 2) AS new_bricks
109+ FROM projects
110+ WHERE COALESCE(bricks_earned, 0) != ROUND(COALESCE(approved_hours, 0) * $1, 2)
111+ ),
112+ corrected AS (
113+ UPDATE projects
114+ SET bricks_earned = mismatched.new_bricks,
115+ updated_at = NOW()
116+ FROM mismatched
117+ WHERE projects.id = mismatched.id
118+ RETURNING mismatched.user_id, mismatched.new_bricks - mismatched.old_bricks AS delta
119+ ),
120+ deltas AS (
121+ SELECT user_id, SUM(delta) AS delta
122+ FROM corrected
123+ GROUP BY user_id
124+ )
125+ UPDATE users
126+ SET bricks = GREATEST(0, users.bricks + deltas.delta),
127+ updated_at = NOW()
128+ FROM deltas
129+ WHERE users.id = deltas.user_id
130+ ` , [ BRICKS_PER_APPROVED_HOUR ] ) ;
100131 await pool . query ( `
101132 UPDATE projects
102133 SET last_shipped_hours = GREATEST(
@@ -436,7 +467,7 @@ export async function approveAdminReviewProject(adminId, projectId, input = {})
436467 throw new Error ( `Cannot approve ${ newTotalApprovedHours . toFixed ( 2 ) } total hours because only ${ totalLoggedHours . toFixed ( 2 ) } hours are logged.` ) ;
437468 }
438469
439- const bricksDelta = approvedHours * 20 ;
470+ const bricksDelta = approvedHours * BRICKS_PER_APPROVED_HOUR ;
440471 const reductionHours = Math . max ( 0 , roundedHours ( totalLoggedHours - newTotalApprovedHours ) ) ;
441472 const newCumulativeBricks = Number ( project . bricks_earned ?? 0 ) + bricksDelta ;
442473
@@ -506,8 +537,9 @@ export async function rejectAdminReviewProject(adminId, projectId, input = {}) {
506537 await client . query ( "BEGIN" ) ;
507538 await refreshProjectJournalHoursWithClient ( client , projectId ) ;
508539
509- const lookup = await client . query ( "SELECT ship_kind FROM projects WHERE id = $1 FOR UPDATE" , [ projectId ] ) ;
510- const isReship = lookup . rows [ 0 ] ?. ship_kind === "reship" ;
540+ const lookup = await client . query ( "SELECT * FROM projects WHERE id = $1 FOR UPDATE" , [ projectId ] ) ;
541+ const project = lookup . rows [ 0 ] ;
542+ const isReship = project ?. ship_kind === "reship" ;
511543
512544 const result = await client . query (
513545 `
0 commit comments