|
| 1 | +/* |
| 2 | + * Copyright 2026 LiteFarm.org |
| 3 | + * This file is part of LiteFarm. |
| 4 | + * |
| 5 | + * LiteFarm is free software: you can redistribute it and/or modify |
| 6 | + * it under the terms of the GNU General Public License as published by |
| 7 | + * the Free Software Foundation, either version 3 of the License, or |
| 8 | + * (at your option) any later version. |
| 9 | + * |
| 10 | + * LiteFarm is distributed in the hope that it will be useful, |
| 11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | + * GNU General Public License for more details, see <https://www.gnu.org/licenses/>. |
| 14 | + */ |
| 15 | + |
| 16 | +/** |
| 17 | + * Reorganizes the certification tables to support multiple certifications per farm: |
| 18 | + * - Renames `certifications` → `certification_system_type` with column renames |
| 19 | + * - Renames `organicCertifierSurvey` → `certification` with column renames and new fields |
| 20 | + * - Soft-deletes `interested = false` rows (no previously deleted rows exist, so `down` can safely reverse this) |
| 21 | + * |
| 22 | + * @param { import("knex").Knex } knex |
| 23 | + * @returns { Promise<void> } |
| 24 | + */ |
| 25 | +export const up = async function (knex) { |
| 26 | + // Step 1: Soft-delete interested=false rows before schema changes. |
| 27 | + await knex('organicCertifierSurvey').where({ interested: false }).update({ deleted: true }); |
| 28 | + |
| 29 | + // Step 2: Rename certifications → certification_system_type and rename all columns |
| 30 | + await knex.schema.renameTable('certifications', 'certification_system_type'); |
| 31 | + await knex.schema.alterTable('certification_system_type', (table) => { |
| 32 | + table.renameColumn('certification_id', 'id'); |
| 33 | + table.renameColumn('certification_type', 'name'); |
| 34 | + table.renameColumn('certification_translation_key', 'translation_key'); |
| 35 | + }); |
| 36 | + |
| 37 | + // Recreate certifiers FK referencing the new table and column names |
| 38 | + await knex.schema.alterTable('certifiers', (table) => { |
| 39 | + table.dropForeign('certification_id'); |
| 40 | + table.renameColumn('certification_id', 'system_type_id'); |
| 41 | + table.foreign('system_type_id').references('id').inTable('certification_system_type'); |
| 42 | + }); |
| 43 | + |
| 44 | + // Step 3: Rename organicCertifierSurvey → certification with column renames and new columns |
| 45 | + // Drop constraints |
| 46 | + await knex.raw( |
| 47 | + `ALTER TABLE "organicCertifierSurvey" DROP CONSTRAINT IF EXISTS "organiccertifiersurvey_certification_id_foreign"`, |
| 48 | + ); |
| 49 | + await knex.raw( |
| 50 | + `ALTER TABLE "organicCertifierSurvey" DROP CONSTRAINT IF EXISTS "organiccertifiersurvey_farm_id_unique"`, |
| 51 | + ); |
| 52 | + |
| 53 | + await knex.schema.renameTable('organicCertifierSurvey', 'certification'); |
| 54 | + await knex.schema.alterTable('certification', (table) => { |
| 55 | + table.renameColumn('survey_id', 'id'); |
| 56 | + table.renameColumn('certification_id', 'system_type_id'); |
| 57 | + table.renameColumn('requested_certification', 'requested_system_type'); |
| 58 | + |
| 59 | + table.boolean('is_active').notNullable().defaultTo(false); |
| 60 | + table |
| 61 | + .enu('certification_type', [ |
| 62 | + 'ORGANIC', |
| 63 | + 'BIODYNAMIC', |
| 64 | + 'REGENERATIVE', |
| 65 | + 'CERTIFIED_HUMANE', |
| 66 | + 'FAIR_TRADE', |
| 67 | + 'GRASSFED/PASTURE', |
| 68 | + 'SUSTAINABILITY', |
| 69 | + 'ANIMAL_WELFARE', |
| 70 | + 'NON-GMO', |
| 71 | + 'CARBON/CLIMATE', |
| 72 | + ]) |
| 73 | + .nullable(); |
| 74 | + table.string('certificate_number').nullable(); |
| 75 | + table.string('certificate_member_id').nullable(); |
| 76 | + table.date('issue_date').nullable(); |
| 77 | + table.date('valid_until').nullable(); |
| 78 | + table.text('certificate_document_url').nullable(); |
| 79 | + |
| 80 | + table.dropColumn('interested'); |
| 81 | + }); |
| 82 | + |
| 83 | + // Add FK for system_type_id after columns are in place |
| 84 | + await knex.schema.alterTable('certification', (table) => { |
| 85 | + table.foreign('system_type_id').references('id').inTable('certification_system_type'); |
| 86 | + }); |
| 87 | + |
| 88 | + // Step 4: Rename permissions |
| 89 | + await knex('permissions') |
| 90 | + .where({ name: 'get:organic_certifier_survey' }) |
| 91 | + .update({ name: 'get:certification', description: 'get certification' }); |
| 92 | + await knex('permissions') |
| 93 | + .where({ name: 'add:organic_certifier_survey' }) |
| 94 | + .update({ name: 'add:certification', description: 'add certification' }); |
| 95 | + await knex('permissions') |
| 96 | + .where({ name: 'edit:organic_certifier_survey' }) |
| 97 | + .update({ name: 'edit:certification', description: 'edit certification' }); |
| 98 | + await knex('permissions') |
| 99 | + .where({ name: 'delete:organic_certifier_survey' }) |
| 100 | + .update({ name: 'delete:certification', description: 'delete certification' }); |
| 101 | +}; |
| 102 | + |
| 103 | +/** |
| 104 | + * @param { import("knex").Knex } knex |
| 105 | + * @returns { Promise<void> } |
| 106 | + */ |
| 107 | +export const down = async function (knex) { |
| 108 | + // Restore permissions |
| 109 | + await knex('permissions') |
| 110 | + .where({ name: 'get:certification' }) |
| 111 | + .update({ name: 'get:organic_certifier_survey', description: 'get organic_certifier_survey' }); |
| 112 | + await knex('permissions') |
| 113 | + .where({ name: 'add:certification' }) |
| 114 | + .update({ name: 'add:organic_certifier_survey', description: 'add organic_certifier_survey' }); |
| 115 | + await knex('permissions').where({ name: 'edit:certification' }).update({ |
| 116 | + name: 'edit:organic_certifier_survey', |
| 117 | + description: 'edit organic_certifier_survey', |
| 118 | + }); |
| 119 | + await knex('permissions').where({ name: 'delete:certification' }).update({ |
| 120 | + name: 'delete:organic_certifier_survey', |
| 121 | + description: 'delete organic_certifier_survey', |
| 122 | + }); |
| 123 | + |
| 124 | + // Drop system_type_id FK before renaming columns and table |
| 125 | + await knex.schema.alterTable('certification', (table) => { |
| 126 | + table.dropForeign('system_type_id'); |
| 127 | + }); |
| 128 | + |
| 129 | + await knex.schema.renameTable('certification', 'organicCertifierSurvey'); |
| 130 | + |
| 131 | + await knex.schema.alterTable('organicCertifierSurvey', (table) => { |
| 132 | + table.dropColumn('certificate_document_url'); |
| 133 | + table.dropColumn('valid_until'); |
| 134 | + table.dropColumn('issue_date'); |
| 135 | + table.dropColumn('certificate_member_id'); |
| 136 | + table.dropColumn('certificate_number'); |
| 137 | + table.dropColumn('certification_type'); |
| 138 | + table.dropColumn('is_active'); |
| 139 | + |
| 140 | + table.boolean('interested').notNullable().defaultTo(false); |
| 141 | + table.unique('farm_id'); |
| 142 | + |
| 143 | + table.renameColumn('requested_system_type', 'requested_certification'); |
| 144 | + table.renameColumn('system_type_id', 'certification_id'); |
| 145 | + table.renameColumn('id', 'survey_id'); |
| 146 | + }); |
| 147 | + |
| 148 | + // Drop certifiers FK and rename column before renaming certification_system_type back |
| 149 | + await knex.schema.alterTable('certifiers', (table) => { |
| 150 | + table.dropForeign('system_type_id'); |
| 151 | + table.renameColumn('system_type_id', 'certification_id'); |
| 152 | + }); |
| 153 | + |
| 154 | + await knex.schema.alterTable('certification_system_type', (table) => { |
| 155 | + table.renameColumn('translation_key', 'certification_translation_key'); |
| 156 | + table.renameColumn('name', 'certification_type'); |
| 157 | + table.renameColumn('id', 'certification_id'); |
| 158 | + }); |
| 159 | + await knex.schema.renameTable('certification_system_type', 'certifications'); |
| 160 | + |
| 161 | + // Recreate FKs referencing the restored certifications table |
| 162 | + await knex.schema.alterTable('certifiers', (table) => { |
| 163 | + table.foreign('certification_id').references('certification_id').inTable('certifications'); |
| 164 | + }); |
| 165 | + // Recreate organicCertifierSurvey FK (dropped in up() before the table was renamed) |
| 166 | + await knex.schema.alterTable('organicCertifierSurvey', (table) => { |
| 167 | + table.foreign('certification_id').references('certification_id').inTable('certifications'); |
| 168 | + }); |
| 169 | + |
| 170 | + // Restore soft-deleted rows: all deleted=true rows were soft-deleted in up() (no prior deletes existed). |
| 171 | + // The interested column was re-added with defaultTo(false), so non-deleted rows need interested=true restored. |
| 172 | + await knex('organicCertifierSurvey').where({ deleted: false }).update({ interested: true }); |
| 173 | + await knex('organicCertifierSurvey').where({ deleted: true }).update({ deleted: false }); |
| 174 | +}; |
0 commit comments