class ReceiptsTable extends TripForeignKeyAbstractSqlTable<Receipt> {

    @Override
    public synchronized void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion, @NonNull TableDefaultsCustomizer customizer) {
        super.onUpgrade(db, oldVersion, newVersion, customizer);

        if (oldVersion <= 1) { // Add mCurrency column to receipts table
            final String alterReceipts = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_ISO4217 + " TEXT NOT NULL " + "DEFAULT " + userPreferenceManager.get(UserPreference.General.DefaultCurrency);

            Logger.debug(this, alterReceipts);

            db.execSQL(alterReceipts);
        }

        if (oldVersion <= 3) { // Add extra_edittext columns
            final String alterReceipts1 = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_1 + " TEXT";
            final String alterReceipts2 = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_2 + " TEXT";
            final String alterReceipts3 = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_3 + " TEXT";

            Logger.debug(this, alterReceipts1);
            Logger.debug(this, alterReceipts2);
            Logger.debug(this, alterReceipts3);

            db.execSQL(alterReceipts1);
            db.execSQL(alterReceipts2);
            db.execSQL(alterReceipts3);
        }

        if (oldVersion <= 4) { // Change Mileage to Decimal instead of Integer
            final String alterReceipts = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_TAX + " DECIMAL(10, 2) DEFAULT 0.00";

            Logger.debug(this, alterReceipts);

            db.execSQL(alterReceipts);
        }


        if (oldVersion <= 6) { // Fix the database to replace absolute paths with relative ones

            Cursor receiptsCursor = null;
            try {
                receiptsCursor = db.query(ReceiptsTable.TABLE_NAME, new String[]{ReceiptsTable.COLUMN_ID, ReceiptsTable.COLUMN_PARENT, ReceiptsTable.COLUMN_PATH}, null, null, null, null, null);
                if (receiptsCursor != null && receiptsCursor.moveToFirst()) {
                    final int idIdx = receiptsCursor.getColumnIndex(ReceiptsTable.COLUMN_ID);
                    final int parentIdx = receiptsCursor.getColumnIndex(ReceiptsTable.COLUMN_PARENT);
                    final int imgIdx = receiptsCursor.getColumnIndex(ReceiptsTable.COLUMN_PATH);
                    do {
                        final int id = receiptsCursor.getInt(idIdx);
                        String absParentPath = receiptsCursor.getString(parentIdx);
                        if (absParentPath.endsWith(File.separator)) {
                            absParentPath = absParentPath.substring(0, absParentPath.length() - 1);
                        }
                        final String absImgPath = receiptsCursor.getString(imgIdx);
                        final ContentValues receiptValues = new ContentValues(2);
                        final String relParentPath = absParentPath.substring(absParentPath.lastIndexOf(File.separatorChar) + 1, absParentPath.length());
                        receiptValues.put(ReceiptsTable.COLUMN_PARENT, relParentPath);
                        Logger.debug(this, "Updating Abs. Parent Path for Receipt{}: {} => {}", id, absParentPath, relParentPath);

                        if (!absImgPath.equalsIgnoreCase(DatabaseHelper.NO_DATA)) { // This can be either a path or NO_DATA
                            final String relImgPath = absImgPath.substring(absImgPath.lastIndexOf(File.separatorChar) + 1, absImgPath.length());
                            receiptValues.put(ReceiptsTable.COLUMN_PATH, relImgPath);
                            Logger.debug(this, "Updating Abs. Img Path for Receipt{}: {} => {}", id, absImgPath, relImgPath);
                        }
                        if (db.update(ReceiptsTable.TABLE_NAME, receiptValues, ReceiptsTable.COLUMN_ID + " = ?", new String[]{Integer.toString(id)}) == 0) {
                            Logger.error(this, "Receipt Update Error Occurred");
                        }
                    }
                    while (receiptsCursor.moveToNext());
                }
            } finally {
                if (receiptsCursor != null) {
                    receiptsCursor.close();
                }
            }
        }
        if (oldVersion <= 7) { // Added a timezone column to the receipts table
            final String alterReceipts = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_TIMEZONE + " TEXT";

            Logger.debug(this, alterReceipts);

            db.execSQL(alterReceipts);
        }
        if (oldVersion <= 11) { // Added trips filters, payment methods, and mileage table
            final String alterReceipts = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_PAYMENT_METHOD_ID + " INTEGER REFERENCES " + PaymentMethodsTable.TABLE_NAME + " ON DELETE NO ACTION";

            Logger.debug(this, alterReceipts);

            db.execSQL(alterReceipts);
        }
        if (oldVersion <= 12) { //Added better distance tracking, cost center to the trips, and status to trips/receipts

            final String alterReceiptsWithProcessingStatus = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_PROCESSING_STATUS + " TEXT";

            Logger.debug(this, alterReceiptsWithProcessingStatus);

            db.execSQL(alterReceiptsWithProcessingStatus);
        }
        if (oldVersion <= 13) {
            final String alterReceipts = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_EXCHANGE_RATE + " DECIMAL(10, 10) DEFAULT -1.00";
            Logger.debug(this, alterReceipts);
            db.execSQL(alterReceipts);
        }
        if (oldVersion <= 14) {
            onUpgradeToAddSyncInformation(db, oldVersion, newVersion);
        }
        if (oldVersion <= 15) { // Changed Categories foreign key from category's Name to Id, added new custom_order_id column

            final String OLD_COLUMN_CATEGORY = "category";

            // adding category id as a foreign key
            final String addCategoryIdColumn = "ALTER TABLE " + ReceiptsTable.TABLE_NAME +
                    " ADD " + ReceiptsTable.COLUMN_CATEGORY_ID + " INTEGER REFERENCES " + CategoriesTable.TABLE_NAME + " ON DELETE NO ACTION";
            Logger.debug(this, addCategoryIdColumn);
            db.execSQL(addCategoryIdColumn);

            final String fillCategoryId = String.format("UPDATE %s SET %s = ( SELECT %s FROM %s WHERE %s = %s LIMIT 1 )",
                    ReceiptsTable.TABLE_NAME, COLUMN_CATEGORY_ID, CategoriesTable.COLUMN_ID, CategoriesTable.TABLE_NAME,
                    CategoriesTable.COLUMN_NAME, OLD_COLUMN_CATEGORY);
            Logger.debug(this, fillCategoryId);
            db.execSQL(fillCategoryId);

            // removing category's name column
            final String copyTable = "CREATE TABLE " + ReceiptsTable.TABLE_NAME + "_copy" + " ("
                    + ReceiptsTable.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                    + ReceiptsTable.COLUMN_PATH + " TEXT, "
                    + ReceiptsTable.COLUMN_PARENT + " TEXT REFERENCES " + TripsTable.TABLE_NAME + " ON DELETE CASCADE, "
                    + ReceiptsTable.COLUMN_NAME + " TEXT DEFAULT \"New Receipt\", "
                    + ReceiptsTable.COLUMN_CATEGORY_ID + " INTEGER REFERENCES " + CategoriesTable.TABLE_NAME + " ON DELETE NO ACTION, "
                    + ReceiptsTable.COLUMN_DATE + " DATE DEFAULT (DATE('now', 'localtime')), "
                    + ReceiptsTable.COLUMN_TIMEZONE + " TEXT, "
                    + ReceiptsTable.COLUMN_COMMENT + " TEXT, "
                    + ReceiptsTable.COLUMN_ISO4217 + " TEXT NOT NULL, "
                    + ReceiptsTable.COLUMN_PRICE + " DECIMAL(10, 2) DEFAULT 0.00, "
                    + ReceiptsTable.COLUMN_TAX + " DECIMAL(10, 2) DEFAULT 0.00, "
                    + ReceiptsTable.COLUMN_EXCHANGE_RATE + " DECIMAL(10, 10) DEFAULT -1.00, "
                    + ReceiptsTable.COLUMN_PAYMENT_METHOD_ID + " INTEGER REFERENCES " + PaymentMethodsTable.TABLE_NAME + " ON DELETE NO ACTION, "
                    + ReceiptsTable.COLUMN_REIMBURSABLE + " BOOLEAN DEFAULT 1, "
                    + ReceiptsTable.COLUMN_NOTFULLPAGEIMAGE + " BOOLEAN DEFAULT 1, "
                    + ReceiptsTable.COLUMN_PROCESSING_STATUS + " TEXT, "
                    + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_1 + " TEXT, "
                    + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_2 + " TEXT, "
                    + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_3 + " TEXT, "
                    + AbstractSqlTable.COLUMN_DRIVE_SYNC_ID + " TEXT, "
                    + AbstractSqlTable.COLUMN_DRIVE_IS_SYNCED + " BOOLEAN DEFAULT 0, "
                    + AbstractSqlTable.COLUMN_DRIVE_MARKED_FOR_DELETION + " BOOLEAN DEFAULT 0, "
                    + AbstractSqlTable.COLUMN_LAST_LOCAL_MODIFICATION_TIME + " DATE, "
                    + AbstractSqlTable.COLUMN_CUSTOM_ORDER_ID + " INTEGER DEFAULT 0"
                    + ");";
            Logger.debug(this, copyTable);
            db.execSQL(copyTable);

            final String finalColumns = TextUtils.join(", ", new String[]{
                    ReceiptsTable.COLUMN_ID, ReceiptsTable.COLUMN_PATH, ReceiptsTable.COLUMN_PARENT,
                    ReceiptsTable.COLUMN_NAME, ReceiptsTable.COLUMN_CATEGORY_ID, ReceiptsTable.COLUMN_DATE,
                    ReceiptsTable.COLUMN_TIMEZONE, ReceiptsTable.COLUMN_COMMENT, ReceiptsTable.COLUMN_ISO4217,
                    ReceiptsTable.COLUMN_PRICE, ReceiptsTable.COLUMN_TAX, ReceiptsTable.COLUMN_EXCHANGE_RATE,
                    ReceiptsTable.COLUMN_PAYMENT_METHOD_ID, ReceiptsTable.COLUMN_REIMBURSABLE,
                    ReceiptsTable.COLUMN_NOTFULLPAGEIMAGE, ReceiptsTable.COLUMN_PROCESSING_STATUS,
                    ReceiptsTable.COLUMN_EXTRA_EDITTEXT_1, ReceiptsTable.COLUMN_EXTRA_EDITTEXT_2,
                    ReceiptsTable.COLUMN_EXTRA_EDITTEXT_3, AbstractSqlTable.COLUMN_DRIVE_SYNC_ID,
                    AbstractSqlTable.COLUMN_DRIVE_IS_SYNCED, AbstractSqlTable.COLUMN_DRIVE_MARKED_FOR_DELETION,
                    AbstractSqlTable.COLUMN_LAST_LOCAL_MODIFICATION_TIME});

            moveDataToCopyTableAndRename(db, finalColumns);

        }
        if (oldVersion <= 18) { // v18 => 19 Changed Trip foreign key from Name to Id, added UUID column

            // adding parent trip id as a foreign key

            final String addTripIdColumn = String.format("ALTER TABLE %s ADD %s INTEGER REFERENCES %s ON DELETE CASCADE",
                    TABLE_NAME, COLUMN_PARENT_TRIP_ID, TripsTable.TABLE_NAME);
            Logger.debug(this, addTripIdColumn);
            db.execSQL(addTripIdColumn);

            final String fillTripId = String.format("UPDATE %s SET %s = ( SELECT %s FROM %s WHERE %s = %s LIMIT 1 )",
                    ReceiptsTable.TABLE_NAME, COLUMN_PARENT_TRIP_ID, TripsTable.COLUMN_ID, TripsTable.TABLE_NAME,
                    TripsTable.COLUMN_NAME, COLUMN_PARENT);
            Logger.debug(this, fillTripId);
            db.execSQL(fillTripId);

            // removing old COLUMN_PARENT column

            final String finalColumns = TextUtils.join(",", new String[]{
                    COLUMN_ID, COLUMN_PATH, COLUMN_PARENT_TRIP_ID, COLUMN_NAME, COLUMN_CATEGORY_ID, COLUMN_DATE,
                    COLUMN_TIMEZONE, COLUMN_COMMENT, COLUMN_ISO4217, COLUMN_PRICE, COLUMN_TAX, COLUMN_EXCHANGE_RATE,
                    COLUMN_PAYMENT_METHOD_ID, COLUMN_REIMBURSABLE, COLUMN_NOTFULLPAGEIMAGE, COLUMN_PROCESSING_STATUS,
                    COLUMN_EXTRA_EDITTEXT_1, COLUMN_EXTRA_EDITTEXT_2, COLUMN_EXTRA_EDITTEXT_3,
                    AbstractSqlTable.COLUMN_DRIVE_SYNC_ID, AbstractSqlTable.COLUMN_DRIVE_IS_SYNCED,
                    AbstractSqlTable.COLUMN_DRIVE_MARKED_FOR_DELETION, AbstractSqlTable.COLUMN_LAST_LOCAL_MODIFICATION_TIME});

            final String copyTable = "CREATE TABLE " + ReceiptsTable.TABLE_NAME + "_copy" + " ("
                    + ReceiptsTable.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                    + ReceiptsTable.COLUMN_PATH + " TEXT, "
                    + ReceiptsTable.COLUMN_PARENT_TRIP_ID + " INTEGER REFERENCES " + TripsTable.TABLE_NAME + " ON DELETE CASCADE, "
                    + ReceiptsTable.COLUMN_NAME + " TEXT DEFAULT \"New Receipt\", "
                    + ReceiptsTable.COLUMN_CATEGORY_ID + " INTEGER REFERENCES " + CategoriesTable.TABLE_NAME + " ON DELETE NO ACTION, "
                    + ReceiptsTable.COLUMN_DATE + " DATE DEFAULT (DATE('now', 'localtime')), "
                    + ReceiptsTable.COLUMN_TIMEZONE + " TEXT, "
                    + ReceiptsTable.COLUMN_COMMENT + " TEXT, "
                    + ReceiptsTable.COLUMN_ISO4217 + " TEXT NOT NULL, "
                    + ReceiptsTable.COLUMN_PRICE + " DECIMAL(10, 2) DEFAULT 0.00, "
                    + ReceiptsTable.COLUMN_TAX + " DECIMAL(10, 2) DEFAULT 0.00, "
                    + ReceiptsTable.COLUMN_EXCHANGE_RATE + " DECIMAL(10, 10) DEFAULT -1.00, "
                    + ReceiptsTable.COLUMN_PAYMENT_METHOD_ID + " INTEGER REFERENCES " + PaymentMethodsTable.TABLE_NAME + " ON DELETE NO ACTION, "
                    + ReceiptsTable.COLUMN_REIMBURSABLE + " BOOLEAN DEFAULT 1, "
                    + ReceiptsTable.COLUMN_NOTFULLPAGEIMAGE + " BOOLEAN DEFAULT 1, "
                    + ReceiptsTable.COLUMN_PROCESSING_STATUS + " TEXT, "
                    + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_1 + " TEXT, "
                    + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_2 + " TEXT, "
                    + ReceiptsTable.COLUMN_EXTRA_EDITTEXT_3 + " TEXT, "
                    + AbstractSqlTable.COLUMN_DRIVE_SYNC_ID + " TEXT, "
                    + AbstractSqlTable.COLUMN_DRIVE_IS_SYNCED + " BOOLEAN DEFAULT 0, "
                    + AbstractSqlTable.COLUMN_DRIVE_MARKED_FOR_DELETION + " BOOLEAN DEFAULT 0, "
                    + AbstractSqlTable.COLUMN_LAST_LOCAL_MODIFICATION_TIME + " DATE, "
                    + AbstractSqlTable.COLUMN_CUSTOM_ORDER_ID + " INTEGER DEFAULT 0"
                    + ");";
            Logger.debug(this, copyTable);
            db.execSQL(copyTable);

            moveDataToCopyTableAndRename(db, finalColumns);

            // adding new UUID column
            onUpgradeToAddUUID(db, oldVersion);
        }

        if (oldVersion <= 19) {
            final String alterReceipts = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_NAME_HIDDEN_AUTO_COMPLETE + " BOOLEAN DEFAULT 0";
            final String alterReceipts2 = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_COMMENT_HIDDEN_AUTO_COMPLETE + " BOOLEAN DEFAULT 0";

            Logger.debug(this, alterReceipts);
            Logger.debug(this, alterReceipts2);

            db.execSQL(alterReceipts);
            db.execSQL(alterReceipts2);
        }

        if (oldVersion <= 20) { // add Tax2 column
            final String addTax2Column = "ALTER TABLE " + ReceiptsTable.TABLE_NAME + " ADD " + ReceiptsTable.COLUMN_TAX2 + " DECIMAL(10, 2) DEFAULT 0.00";

            Logger.debug(this, addTax2Column);

            db.execSQL(addTax2Column);
        }

    }

    public  ReceiptsTable(@NonNull SQLiteOpenHelper sqLiteOpenHelper,
                         @NonNull Table<Trip> tripsTable,
                         @NonNull Table<PaymentMethod> paymentMethodTable,
                         @NonNull Table<Category> categoryTable,
                         @NonNull StorageManager storageManager,
                         @NonNull UserPreferenceManager preferences,
                         @NonNull OrderingPreferencesManager orderingPreferencesManager);

}