diff --git a/packages/api/db/migration/20240202225512_harvest_task_wage_fixes.js b/packages/api/db/migration/20240202225512_harvest_task_wage_fixes.js new file mode 100644 index 0000000000..e302dcb8b7 --- /dev/null +++ b/packages/api/db/migration/20240202225512_harvest_task_wage_fixes.js @@ -0,0 +1,135 @@ +/* + * Copyright 2024 LiteFarm.org + * This file is part of LiteFarm. + * + * LiteFarm is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LiteFarm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details, see . + */ + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +export const up = async function (knex) { + // create an audit table + await knex.schema.createTable('audit_log', (table) => { + table.increments('id').primary(); + table.string('record_id').notNullable(); // expect string, number or UUID + table.string('table_name').notNullable(); + table.string('column_name').notNullable(); + table.string('old_value').nullable(); + table.string('new_value').nullable(); + table.integer('user_id').defaultTo(1); + table.timestamp('timestamp').defaultTo(knex.fn.now()); + }); + + try { + const incompleteTasksToBeUpdated = await knex.raw(` + SELECT * + FROM task t + WHERE task_type_id = 8 + AND abandon_date is NULL + AND complete_date is NULL + AND override_hourly_wage is false + AND wage_at_moment is NOT NULL + AND t.created_at < '2023-11-21 10:23' + AND t.deleted = FALSE + AND t.assignee_user_id is NULL; + `); + + await knex.transaction(async (trx) => { + for (const task of incompleteTasksToBeUpdated.rows) { + const { task_id, override_hourly_wage } = task; + + // Update incomplete task + await knex('task') + .where({ task_id }) + .update({ override_hourly_wage: true }) + .then(async () => { + // Insert a row to the audit table + await trx('audit_log').insert({ + record_id: task_id, + table_name: 'task', + column_name: 'override_hourly_wage', // the updated column + old_value: override_hourly_wage, + new_value: true, + }); + }); + } + }); + + // Retrieve completed harvest tasks that do not have wage_at_moment + const completedTasksToBeUpdated = await knex.raw(` + SELECT t.task_id, t.wage_at_moment, "userFarm".wage + FROM task t + JOIN location_tasks ON t.task_id = location_tasks.task_id + JOIN location ON location_tasks.location_id = location.location_id + LEFT JOIN "userFarm" ON location.farm_id = "userFarm".farm_id + AND ("userFarm".user_id = t.assignee_user_id OR (t.assignee_user_id IS NULL AND "userFarm".user_id IS NULL)) + JOIN users ON t.owner_user_id = users.user_id + WHERE task_type_id = 8 + AND override_hourly_wage = FALSE + AND complete_date is not NULL + AND (t.created_at > '2021-10-31' AND t.created_at < '2023-11-21') + AND wage_at_moment is NULL + `); + + await knex.transaction(async (trx) => { + for (const task of completedTasksToBeUpdated.rows) { + const { task_id, wage_at_moment, wage } = task; + const newWage = wage.amount || 0; + + // Update incomplete task + await trx('task') + .where({ task_id }) + .update({ wage_at_moment: newWage }) + .then(async () => { + // Insert a row to the audit table + await trx('audit_log').insert({ + record_id: task_id, + table_name: 'task', + column_name: 'wage_at_moment', // the updated column + old_value: wage_at_moment, + new_value: newWage, + }); + }); + } + }); + } catch (error) { + console.error('Error in rollback:', error); + throw error; // Rethrow the error to ensure the migration fails + } +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +export const down = async function (knex) { + try { + await knex.transaction(async (trx) => { + const rows = await knex.select().from('audit_log'); + + // Rollback modified tasks + for (const row of rows) { + const { record_id, column_name, old_value } = row; + + await trx('task') + .where({ task_id: record_id }) + .update({ [column_name]: old_value }); + } + }); + } catch (error) { + console.error('Error in rollback:', error); + throw error; // Rethrow the error to ensure the migration fails + } + + await knex.schema.dropTable('audit_log'); +};