drizzle-team / drizzle-orm

Headless TypeScript ORM with a head. Runs on Node, Bun and Deno. Lives on the Edge and yes, it's a JavaScript ORM too 😅
https://orm.drizzle.team
Apache License 2.0
24.3k stars 627 forks source link

Error on `drizzle-kit push`, Cannot read properties of undefined (reading 'type') #3369

Open FranCanias opened 5 months ago

FranCanias commented 5 months ago

Hi! i have this error when i do 'npx drizzle-kit push', i fixed it adding a question mark on 'node_modules/drizzle-kit/bin.cjs:111813:123'

this is the following error im getting in the terminal:

npx drizzle-kit push               
drizzle-kit: v0.21.4
drizzle-orm: v0.30.10

No config path provided, using default path
Reading config file '/Users/x/x/x/drizzle.config.ts'
[✓] Pulling schema from database...Reading schema files:
/Users/x/x/x/src/server/schema.ts

TypeError: Cannot read properties of undefined (reading 'type')
    at /Users/x/x/x/node_modules/drizzle-kit/bin.cjs:111813:123
    at Array.filter (<anonymous>)
    at filterStatements (/Users/x/x/x/node_modules/drizzle-kit/bin.cjs:111793:25)
    at mysqlPush (/Users/x/x/x/node_modules/drizzle-kit/bin.cjs:114380:34)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Command.<anonymous> (/Users/x/x/x/node_modules/drizzle-kit/bin.cjs:121465:7)

npx: 10.2.4 node: v20.11.0

the line i modified to fix it is the following:

if (unsquashed.columns.length === 1 && currentSchema.tables[statement.tableName].columns[unsquashed.columns[0]]?/* <---- Added question mark */.type === "serial" && prevSchema.tables[statement.tableName].columns[unsquashed.columns[0]].type === "serial" && currentSchema.tables[statement.tableName].columns[unsquashed.columns[0]].name === unsquashed.columns[0]) {
PurpleTape commented 5 months ago

Also faced with it

> drizzle-kit push

drizzle-kit: v0.21.4
drizzle-orm: v0.30.10

No config path provided, using default path
Reading config file '/my-app-path/drizzle.config.ts'
Using 'pg' driver for database querying
[✓] Pulling schema from database...TypeError: Cannot use 'in' operator to search for 'default' in undefined
    at /my-app-path/node_modules/drizzle-kit/bin.cjs:18362:23
    at Array.map (<anonymous>)
    at alternationsInColumn (/my-app-path/node_modules/drizzle-kit/bin.cjs:18361:10)
    at /my-app-path/node_modules/drizzle-kit/bin.cjs:18321:49
    at Array.map (<anonymous>)
    at findAlternationsInTable (/my-app-path/node_modules/drizzle-kit/bin.cjs:18321:37)
    at /my-app-path/node_modules/drizzle-kit/bin.cjs:18203:14
    at Array.map (<anonymous>)
    at applyJsonDiff (/my-app-path/node_modules/drizzle-kit/bin.cjs:18201:69)
    at applyPgSnapshotsDiff (/my-app-path/node_modules/drizzle-kit/bin.cjs:19822:26)
AlexBlokh commented 5 months ago

please provide database/typescript drizzle schema

FranCanias commented 5 months ago

The schema its kinda big, i leave it here

import { mysqlTableCreator, primaryKey, unique, varchar, int, text, json, tinyint, datetime, date, time, serial, timestamp, float, boolean, double, MySqlEnumColumn, mysqlEnum, bigint, MySqlTextBuilder } from 'drizzle-orm/mysql-core'
import { relations, sql, type InferSelectModel, type SQL } from 'drizzle-orm'
import { customType } from 'drizzle-orm/mysql-core'
import { z } from 'zod'
import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
// #region TYPES
const ECMysqlTable = mysqlTableCreator((name) => `EC_${name}`)

const mysqlTable = mysqlTableCreator((name) => `PANEL_${name}`)
// #region EASYCHARTER

// #region CustomDrivers

type SQLValues = { coordinates: number[] }
type SQLValuesArray = { coordinates: number[][] }
type SQLPolygon = [number, number][][]

const point = <TData extends { lat: number; lng: number }>(name: string) =>
  customType<{ data: TData; driverData: SQLValues }>({
    dataType() {
      return 'point'
    },
    toDriver(value: TData): SQL {
      return sql`ST_GeomFromText('POINT(${value.lat} ${value.lng})',4326)`
    },
    fromDriver(value: SQLValues): TData {
      const points = value.coordinates
      return { lat: points?.[1] ?? 0, lng: points?.[0] ?? 0 } as TData
    },
  })(name)

const lineString = <TData extends { lat: number; lng: number }[]>(name: string) =>
  customType<{ data: TData; driverData: SQLValuesArray }>({
    dataType() {
      return 'linestring'
    },
    toDriver(value: TData): SQL {
      const data = 'LINESTRING(' + value.map((point) => point.lat + ' ' + point.lng).join() + ')'
      return sql`ST_GeomFromText(${data},4326)`
    },
    fromDriver(value: SQLValuesArray): TData {
      const linestring = value.coordinates
      const latLng = linestring.map((point) => {
        return { lat: point[1], lng: point[0] }
      })
      return latLng as TData
    },
  })(name)

export const polygon = <TData extends { lat: number; lng: number }[]>(name: string) =>
  customType<{ data: TData; driverData: SQLPolygon }>({
    dataType() {
      return 'polygon'
    },
    toDriver(value: TData): SQL {
      const data = 'POLYGON((' + value.map((point) => point.lat + ' ' + point.lng).join() + '))'
      return sql`ST_GeomFromText(${data},4326)`
    },
  })(name)

// #endregion CustomDrivers

// #region Account
export const ecaccount = mysqlTable(
  'Account',
  {
    id: varchar('id', { length: 191 })
      .notNull()
      .default(sql`(uuid())`),
    userId: varchar('userId', { length: 191 }).notNull(),
    type: varchar('type', { length: 191 }).notNull(),
    provider: varchar('provider', { length: 191 }).notNull(),
    providerAccountId: varchar('providerAccountId', { length: 191 }).notNull(),
    refreshToken: varchar('refresh_token', { length: 191 }),
    accessToken: varchar('access_token', { length: 191 }),
    expiresAt: int('expires_at'),
    tokenType: varchar('token_type', { length: 191 }),
    scope: varchar('scope', { length: 191 }),
    idToken: varchar('id_token', { length: 191 }),
    sessionState: varchar('session_state', { length: 191 }),
  },
  (table) => {
    return {
      accountId: primaryKey({columns: [table.id], name: 'PANEL_Account_id'}),
      accountProviderProviderAccountIdKey: unique('Account_provider_providerAccountId_key').on(table.provider, table.providerAccountId),
    }
  }
)
// #endregion Account
// #region Boat
// #region BoatTypes

export enum EQUIPMENT_CATEGORY {
  EXTERIOR = 'Exterior',
  COMFORT = 'Comfort',
  NAVIGATION = 'Navegacion',
  COOKING = 'Cocina',
  RECREATIVE = 'Ocio',
}
// #endregion BoatTypes
export const boat = mysqlTable(
  'Boat',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').notNull(),
    status: tinyint('status').$type<0 | 1 | 2>().default(0).notNull(),
    photos: json('photos').$type<string[]>(),
    bail: int('bail').default(0),
    contractId: int('contractId'),
    description: text('description'),
    length: text('length'),
    lengthCopy: float('lengthCopy'),
    width: text('width'),
    capacity: int('capacity'),
    cabins: int('cabins'),
    weight: int('weight'),
    maxWeight: int('maxWeight'),
    licence: text('licence'),
    flag: text('flag'),
    motors: int('motors'),
    motorType: text('motorType'),
    motorMaker: text('motorMaker'),
    motorPower: int('motorPower'),
    maxSpeed: int('maxSpeed'),
    cruiseSpeed: int('cruiseSpeed'),
    placeId: text('placeId'),
    placesId: int('placesId'),
    position: point('position').$type<{ lat: number; lng: number }>(),
    positionObj: json('positionObj').$type<{lat: number, lng: number}>(),
    type: text('type').$type<'Sin Definir' | 'Lancha' | 'Llaut' | 'Neumatico' | 'Velero' | 'Catamaran' | 'Semirigida' | 'Yate'>().default('Sin Definir'),
    insuranceDoc: text('insuranceDoc'),
    insuranceProvider: text('insuranceProvider'),
    insuranceStart: date('insuranceStart'),
    insuranceEnd: date('insuranceEnd'),
    prepTime: int('prepTime'),
    details: json('details').$type<{
      place?: string
      maker?: string
      model?: string
      year?: string
      registration?: string
      renovation?: string
      fuel?: string
      engine?: number
      rooms?: number
      beds?: number
    }>(),
    includesCaptain: boolean('includesCaptain').default(false),
    seasons: json('seasons').$type<
      {
        id: number
        status: number
        rates: {
          duration: number
          price: number
          captain?: number
        }[]
      }[]
    >(),
    verifications: json('verifications').$type<
      {
        name: string
        image?: string
        description: string
        price: number
        when: 'checkin' | 'checkout' | 'both'
      }[]
    >(),
    gpsOptions: json('gpsOptions'),
    extras: json('extras').$type<number[]>(),
    services: json('services').$type<{ id: number; variant: number }[]>(),
    equipment: json('equipment').$type<string[]>(),
    groupId: int('groupId'),
    companyId: text('companyId').notNull(),
    createdAt: timestamp('createdAt').default(sql`now()`),
    fees: json('fees').$type<{ halfday: number; fullday: number; sunset: number }>(),
    increment: json('increment').$type<{ halfday: number; fullday: number; sunset: number }>(),
  },
  (table) => {
    return {
      boatId: primaryKey({ columns: [table.id], name: 'PANEL_Boat_id'}),
    }
  }
)

export const boatConfig = relations(boat, ({ one, many }) => ({
  reserves: many(reserve),
  positions: many(position),
  finances: many(finance),
  group: one(boat, { fields: [boat.groupId], references: [boat.id], relationName: 'group' }),
  child: many(boat, { relationName: 'group' }),
  company: one(company, { fields: [boat.companyId], references: [company.id] }),
  documents: many(document),
  contract: one(contract, { fields: [boat.contractId], references: [contract.id] }),
  tasks: many(task),
  shopItems: many(shopItem),
  discounts: many(discountToBoat),
  place: one(place, { fields: [boat.placesId], references: [place.id] }),
}))

export const insetBoatSchema = createInsertSchema(boat, {
  type: z.literal('Sin Definir').or(z.literal('Lancha')).or(z.literal('Llaut')).or(z.literal('Neumatico')).or(z.literal('Velero')).or(z.literal('Catamaran')).or(z.literal('Yate')).or(z.literal('Semirigida')).optional(),
  status: z.literal(0).or(z.literal(1)).or(z.literal(2)).optional(),
  photos: z.array(z.string()).optional(),
  positionObj: z.object({ lat: z.number(), lng: z.number() }).optional(),
  details: z
    .object({
      place: z.string().optional(),
      maker: z.string().optional(),
      model: z.string().optional(),
      year: z.string().optional(),
      registration: z.string().optional(),
      renovation: z.string().optional(),
      fuel: z.string().optional(),
      engine: z.number().optional(),
      rooms: z.number().optional(),
      beds: z.number().optional(),
    })
    .optional(),
  seasons: z
    .array(
      z.object({
        id: z.number(),
        status: z.number(),
        rates: z.array(
          z.object({
            duration: z.number(),
            price: z.number(),
          })
        ),
      })
    )
    .optional(),
  extras: z.array(z.number()).optional(),
  services: z.array(z.object({ id: z.number(), variant: z.number() })).optional(),
  verifications: z
    .array(
      z.object({
        name: z.string(),
        price: z.number(),
        description: z.string(),
        image: z.string().optional(),
        when: z.literal('checkin').or(z.literal('checkout')).or(z.literal('both')),
      })
    )
    .optional(),
  equipment: z.array(z.string()).optional(),
  fees: z.object({ halfday: z.number(), fullday: z.number(), sunset: z.number() }).optional(),
  increment: z.object({ halfday: z.number(), fullday: z.number(), sunset: z.number() }).optional(),
})

export type Boat = InferSelectModel<typeof boat>

export type GpsBoat = InferSelectModel<typeof boat> & {
  positions: InferSelectModel<typeof position>[]
  reserves: GpsReserve[]
}
// #endregion Boat
// #region PartnerBoat
export const partnerBoat = mysqlTable(
  'PartnerBoat',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').notNull(),
    photo: text('photo'),
    description: text('description'),
    length: text('length'),
    capacity: int('capacity'),
    licence: text('licence'),
    type: text('type').$type<'Sin Definir' | 'Lancha' | 'Llaut' | 'Neumatico' | 'Velero' | 'Catamaran' | 'Semirigida' | 'Yate'>().default('Sin Definir'),
    maker: text('maker'),
    model: text('model'),
    engine: int('engine'),
    prices: json('prices').$type<{ halfday: number; fullday: number }>(),
    partnerId: text('partnerId').notNull(),
    createdAt: timestamp('createdAt').default(sql`now()`),
  },
  (table) => {
    return {
      partnerBoatId: primaryKey({columns: [table.id], name: 'PANEL_PartnerBoat_id'}),
    }
  }
)

export const partnerBoatConfig = relations(partnerBoat, ({ one }) => ({
  partner: one(partner, { fields: [partnerBoat.partnerId], references: [partner.id] }),
}))

export const updatePartnerBoatSchema = createSelectSchema(partnerBoat, {
  id: z.number(),
  name: z.string().optional(),
  type: z.literal('Sin Definir').or(z.literal('Lancha')).or(z.literal('Llaut')).or(z.literal('Neumatico')).or(z.literal('Velero')).or(z.literal('Catamaran')).or(z.literal('Semirigida')).or(z.literal('Yate')).optional(),
  prices: z.object({ halfday: z.number(), fullday: z.number() }).optional(),
})
// #endregion PartnerBoat
// #region Company
export const company = mysqlTable(
  'Company',
  {
    id: varchar('id', { length: 191 })
      .notNull()
      .unique()
      .default(sql`(uuid())`),
    name: text('name'),
    legalName: text('legalName'),
    docType: text('docType'),
    document: text('document'),
    address: text('address'),
    addressId: text('addressId'),
    zip: text('zip'),
    phone: text('phone'),
    email: text('email'),
    managerName: text('managerName'),
    managerPhone: text('managerPhone'),
    managerEmail: text('managerEmail'),
    website: text('website'),
    logo: text('logo'),
    timezone: text('timezone'),
    settings: json('settings').$type<{ prepTime: number }>().default({ prepTime: 0 }),
    priority: int('priority').default(0),
    active: boolean('active').default(true),
  },
  (table) => {
    return {
      companyId: primaryKey({columns: [table.id], name: 'PANEL_Extra_idCompany_id'}),
    }
  }
)

export const companyConfig = relations(company, ({ many }) => ({
  reserves: many(reserve),
  boats: many(boat),
  finances: many(finance),
  partners: many(partner),
  seasons: many(season),
  extras: many(extra),
  services: many(service),
  clients: many(client),
  resellers: many(reseller),
  methods: many(method),
  employees: many(ecuser),
  financeCategories: many(financeCategory),
  departments: many(department),
  origins: many(origin),
  discounts: many(discount),
  contracts: many(contract),
}))

export type Company = InferSelectModel<typeof company> & {
  seasons: InferSelectModel<typeof season>[]
  finances: InferSelectModel<typeof finance>[]
  resellers: InferSelectModel<typeof reseller>[]
  employees: InferSelectModel<typeof ecuser>[]
}
// #endregion Company
// #region Extra
export const extra = mysqlTable(
  'Extra',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').default('').notNull(),
    description: text('description'),
    photo: text('photo'),
    status: int('status').$type<0 | 1>().default(0).notNull(),
    type: text('type').$type<'permanent' | 'consumable'>().default('permanent').notNull(),
    variants: json('variants')
      .$type<
        (
          | {
              id: number
              type: 'array'
              name: string
              stock: number
              prices: { duration: number; price: number }[]
            }
          | {
              id: number
              type: 'simple'
              name: string
              stock: number
              price: number
            }
        )[]
      >()
      .notNull()
      .default([]),
    companyId: text('companyId').notNull(),
  },
  (table) => {
    return {
      extrasId: primaryKey({columns: [table.id], name: 'PANEL_Extra_id'}),
    }
  }
)

export const extrasConfig = relations(extra, ({ one }) => ({
  company: one(company, { fields: [extra.companyId], references: [company.id] }),
}))

export type Extra = InferSelectModel<typeof extra>
// #endregion Extra
// #region Document
export const document = mysqlTable(
  'Document',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').notNull(),
    boatId: int('boatId').notNull(),
    name: text('name').default('').notNull(),
    description: text('description'),
    images: json('images').$type<string[]>().default([]),
  },
  (table) => {
    return {
      documentId: primaryKey({columns: [table.id], name: 'PANEL_Document_id'}),
    }
  }
)

export const documentConfig = relations(document, ({ one }) => ({
  boat: one(boat, { fields: [document.boatId], references: [boat.id] }),
  company: one(company, { fields: [document.companyId], references: [company.id] }),
}))
// #endregion Document
// #region Contract

type ContractBlock =
  | {
      id: number
      type: 'paragraph' | 'heading'
      content: string
    }
  | {
      id: number
      type: 'table'
      content: {
        headers: [string, string]
        rows: [string, string][]
      }
    }

export const contract = mysqlTable(
  'Contract',
  {
    id: int('id').notNull().unique().autoincrement(),
    originalId: int('originalId'),
    version: int('version').default(1).notNull(),
    companyId: text('companyId').notNull(),
    name: text('name').default('').notNull(),
    es: json('es').$type<ContractBlock[]>(),
    en: json('en').$type<ContractBlock[]>(),
    fr: json('fr').$type<ContractBlock[]>(),
    de: json('de').$type<ContractBlock[]>(),
    it: json('it').$type<ContractBlock[]>(),
  },
  (table) => {
    return {
      contractId: primaryKey({columns: [table.id], name: 'PANEL_Contract_id'}),
    }
  }
)

export const contractConfig = relations(contract, ({ one, many }) => ({
  company: one(company, { fields: [contract.companyId], references: [company.id] }),
  original: one(contract, { fields: [contract.originalId], references: [contract.id], relationName: 'original' }),
  child: many(contract, { relationName: 'original' }),
}))

export type Contract = InferSelectModel<typeof contract>
// #endregion Contract
// #region Finance
export const finance = mysqlTable(
  'Finance',
  {
    id: int('id').notNull().unique().autoincrement(),
    date: datetime('date', { fsp: 3 })
      .notNull()
      .default(sql`CURRENT_TIMESTAMP(3)`),
    sign: int('sign').notNull(),
    amount: double('amount').notNull(),
    category: text('category'),
    description: text('description'),
    photo: text('photo'),
    refundFrom: int('refundFrom'),
    methodId: int('methodId'),
    boatId: int('boatId'),
    taskId: int('taskId'),
    clientId: bigint('clientId', { mode: 'number' }),
    reserveId: int('reserveId'),
    companyId: varchar('companyId', { length: 191 }).notNull(),
    originId: int('originId'),
    categoryId: int('categoryId'),
    departmentId: int('departmentId'),
    createdBy: text('createdBy'),
    initial: boolean('initial').default(false),
  },
  (table) => {
    return {
      financesId: primaryKey({columns: [table.id], name: 'PANEL_Finance_id'}),
    }
  }
)

export const financesConfig = relations(finance, ({ one }) => ({
  reserve: one(reserve, { fields: [finance.reserveId], references: [reserve.id] }),
  company: one(company, { fields: [finance.companyId], references: [company.id] }),
  boat: one(boat, { fields: [finance.boatId], references: [boat.id] }),
  client: one(client, { fields: [finance.clientId], references: [client.id] }),
  origin: one(origin, { fields: [finance.originId], references: [origin.id] }),
  method: one(method, { fields: [finance.methodId], references: [method.id] }),
  task: one(task, { fields: [finance.taskId], references: [task.id] }),
  refunded: one(finance, { fields: [finance.refundFrom], references: [finance.id] }),
  refund: one(finance, { fields: [finance.id], references: [finance.refundFrom] }),
  department: one(department, { fields: [finance.departmentId], references: [department.id] }),
  categoryRelated: one(financeCategory, { fields: [finance.categoryId], references: [financeCategory.id] }),
  user: one(ecuser, { fields: [finance.createdBy], references: [ecuser.id] }),
}))

export type Finances = InferSelectModel<typeof finance>

export type ReserveFinance = InferSelectModel<typeof finance> & {
  refund: InferSelectModel<typeof finance> & {
    user: InferSelectModel<typeof ecuser> | null
  }| null
  refunded: InferSelectModel<typeof finance> | null
  method: InferSelectModel<typeof method> | null
  user: InferSelectModel<typeof ecuser> | null
}
// #endregion Finance
// #region Geofence
export const geofence = mysqlTable(
  'Geofence',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').notNull().default(''),
    path: polygon('path'),
    point: point('point'),
    radius: float('radius'),
    options: json('options'),
    type: int('type'),
    color: text('color'),
    description: text('description'),
    category: int('category').default(-1),
    interest: boolean('interest').default(false),
    photos: json('photos').$type<string[]>().default([]).notNull(),
    stars: int('stars').default(1).notNull(),
    companyId: varchar('companyId', { length: 191 }).notNull(),
  },
  (table) => {
    return {
      geofenceId: primaryKey({columns: [table.id], name: 'PANEL_Geofence_id'}),
    }
  }
)

export const geofenceConfig = relations(geofence, ({ one }) => ({
  company: one(company, { fields: [geofence.companyId], references: [company.id] }),
}))

export type Geofence = InferSelectModel<typeof geofence>
// #endregion Geofence
// #region Partner
export const partner = mysqlTable(
  'Partner',
  {
    id: varchar('id', { length: 191 })
      .notNull()
      .unique()
      .default(sql`(uuid())`),
    companyId: text('companyId').notNull(),
    name: text('name').notNull(),
    detail: text('detail'),
    logo: text('logo'),
    status: int('status').notNull(),
    priority: int('priority').notNull().default(0),
    place: text('place'),
    placeId: text('placeId'),
    phone: text('phone'),
    email: text('email'),
    website: text('website'),
    instagram: text('instagram'),
    facebook: text('facebook'),
    linkedin: text('linkedin'),
    updatedAt: datetime('updatedAt')
      .default(sql`CURRENT_TIMESTAMP`)
      .notNull(),
    ratings: json('ratings').$type<{ website: number; facebook: number; reserveSystem: number; instagram: number; linkedin: number }>().notNull().default({ website: 0, facebook: 0, reserveSystem: 0, instagram: 0, linkedin: 0 }),
    origin: text('origin'),
    reserveSystem: text('reserveSystem'),
  },
  (table) => {
    return {
      partnersId: primaryKey({columns: [table.id], name: 'PANEL_Partner_id'}),
    }
  }
)

export const partnersConfig = relations(partner, ({ one, many }) => ({
  company: one(company, { fields: [partner.companyId], references: [company.id] }),
  comments: many(partnerComment),
  boats: many(partnerBoat),
}))

export type Partner = InferSelectModel<typeof partner> & {
  boats: InferSelectModel<typeof partnerBoat>[]
}
// #endregion Partner
// #region Position
export const position = mysqlTable(
  'Position',
  {
    id: varchar('id', { length: 191 }).notNull().unique(),
    latest: point('latest').$type<{ lat: number; lng: number }>(),
    pastPoint: point('pastPoint').$type<{ lat: number; lng: number }>(),
    path: lineString('path').$type<{ lat: number; lng: number }[]>(),
    times: json('times').$type<number[]>().default([]).notNull(),
    speed: json('speed').$type<number[]>().default([]).notNull(),
    ignition: json('ignition').$type<boolean[]>().default([]).notNull(),
    createdAt: timestamp('createdAt').default(sql`now()`),
    updatedAt: timestamp('updatedAt')
      .default(sql`now()`)
      .onUpdateNow()
      .notNull(),
    deviceId: int('deviceId'),
    geofences: json('geofences').default([]),
    lastGeofence: json('lastGeofence'),
    pastGeofence: json('pastGeofence'),
  },
  (table) => {
    return {
      positionId: primaryKey({columns: [table.id], name: 'PANEL_Position_id'}),
    }
  }
)

export const positionConfig = relations(position, ({ one }) => ({
  boat: one(boat, { fields: [position.deviceId], references: [boat.id] }),
}))

export type Position = InferSelectModel<typeof position>
// #endregion Position
// #region Reserve
export const reserve = mysqlTable(
  'Reserve',
  {
    id: int('id').notNull().unique().autoincrement(),
    status: int('status').default(0).notNull(),
    departure: datetime('departure', { fsp: 3 }).notNull(),
    duration: int('duration').notNull(),
    checkin: datetime('checkin', { fsp: 3 }),
    checkout: datetime('checkout', { fsp: 3 }),
    tripulation: json('tripulation').$type<{ adults: number; kids: number; babies: number }>().notNull().default({ adults: 0, kids: 0, babies: 0 }),
    comment: text('comment'),
    extras: json('extras').$type<{ id: number; variant: number; amount: number }[]>().default([]).notNull(),
    services: json('services').$type<{ id: number; variant: number; amount: number }[]>().default([]).notNull(),
    requiresCaptain: boolean('requiresCaptain').default(false).notNull(),
    discounts: json('discounts').$type<{ id: number; name: string; code?: string; amount: number; sign: string }[]>().default([]).notNull(),
    contracts: json('contracts').$type<{checkin: {id: number, sign: string, language: string}}>(),
    companyId: text('companyId').notNull(),
    boatId: int('boatId').notNull(),
    clientId: bigint('clientId', { mode: 'number' }),
    resellerId: int('resellerId'),
    originId: int('originId'),
    captainId: varchar('captainId', { length: 191 }),
    createdBy: text('createdBy'),
    ecResellerId: text('ecResellerId'),
    price: float('price'),
  },
  (table) => {
    return {
      reservesId: primaryKey({columns: [table.id], name: 'PANEL_Reserve_id'}),
    }
  }
)

export const reservesConfig = relations(reserve, ({ one, many }) => ({
  finances: many(finance),
  company: one(company, { fields: [reserve.companyId], references: [company.id] }),
  boat: one(boat, { fields: [reserve.boatId], references: [boat.id] }),
  client: one(client, { fields: [reserve.clientId], references: [client.id] }),
  reseller: one(reseller, { fields: [reserve.resellerId], references: [reseller.id] }),
  origin: one(origin, { fields: [reserve.originId], references: [origin.id] }),
  captain: one(ecuser, { fields: [reserve.captainId], references: [ecuser.id], relationName: 'captain' }),
  messages: many(reserveMsg),
  //user: one(user, { fields: [reserve.createdBy], references: [user.id], relationName: 'createdBy' }),
}))

export type Reserve = InferSelectModel<typeof reserve> & {
  finances: InferSelectModel<typeof finance>[] | null
  boat: InferSelectModel<typeof boat> | null
  client: InferSelectModel<typeof client> | null
  reseller: InferSelectModel<typeof reseller> | null
  origin: InferSelectModel<typeof origin> | null
  captain: InferSelectModel<typeof ecuser> | null
}

export type CleanReserve = InferSelectModel<typeof reserve>

export type GpsReserve = InferSelectModel<typeof reserve> & {
  client: InferSelectModel<typeof client>
}
// #endregion Reserve
// #region ReserveMsg
export const reserveMsg = mysqlTable(
  'ReserveMsg',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').notNull(),
    reserveId: int('reserveId').notNull(),
    content: text('content').notNull(),
    createdBy: text('createdBy').notNull(),
    createdAt: timestamp('createdAt')
      .default(sql`now()`)
      .notNull(),
  },
  (table) => {
    return {
      reservesId: primaryKey({columns: [table.id], name: 'PANEL_ReserveMsg_id'}),
    }
  }
)

export const reserveMsgConfig = relations(reserveMsg, ({ one }) => ({
  reserve: one(reserve, { fields: [reserveMsg.reserveId], references: [reserve.id] }),
  user: one(ecuser, { fields: [reserveMsg.createdBy], references: [ecuser.id] }),
}))

// #endregion ReserveMsg
// #region Season
export const season = mysqlTable(
  'Season',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name'),
    start: date('start').notNull(),
    end: date('end').notNull(),
    status: int('status').default(0).notNull(),
    close: int('close').default(0).notNull(),
    open: int('open').default(0).notNull(),
    color: text('color'),
    companyId: text('companyId'),
  },
  (table) => {
    return {
      seasonsId: primaryKey({columns: [table.id], name: 'PANEL_Season_id'}),
    }
  }
)

export const seasonConfig = relations(season, ({ one }) => ({
  company: one(company, { fields: [season.companyId], references: [company.id] }),
}))

export type Season = InferSelectModel<typeof season>
// #endregion Season
// #region Service
export const service = mysqlTable(
  'Service',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').default('').notNull(),
    description: text('description'),
    photo: text('photo'),
    status: int('status').$type<0 | 1>().default(0).notNull(),
    variants: json('variants')
      .$type<
        (
          | {
              id: number
              type: 'array'
              name: string
              stock: number
              prices: { duration: number; price: number }[]
            }
          | {
              id: number
              type: 'simple'
              name: string
              stock: number
              price: number
            }
        )[]
      >()
      .notNull()
      .default([]),
    companyId: text('companyId').notNull(),
  },
  (table) => {
    return {
      servicesId: primaryKey({columns: [table.id], name: 'PANEL_Service_id'}),
    }
  }
)

export const serviceConfig = relations(service, ({ one }) => ({
  company: one(company, { fields: [service.companyId], references: [company.id] }),
}))

export type Service = InferSelectModel<typeof service>
// #endregion Service
// #region Session
export const ecsession = mysqlTable(
  'Session',
  {
    id: varchar('id', { length: 191 })
      .notNull()
      .unique()
      .default(sql`(uuid())`),
    sessionToken: varchar('sessionToken', { length: 191 }).notNull(),
    userId: varchar('userId', { length: 191 }).notNull(),
    expires: datetime('expires', { mode: 'string', fsp: 3 }).notNull(),
  },
  (table) => {
    return {
      sessionId: primaryKey({columns: [table.id], name: 'PANEL_Session_id'}),
      sessionSessionTokenKey: unique('Session_sessionToken_key').on(table.sessionToken),
    }
  }
)
// #endregion Session
// #region User
export const ecuser = mysqlTable(
  'User',
  {
    id: varchar('id', { length: 191 })
      .notNull()
      .primaryKey()
      .default(sql`(uuid())`),
    name: varchar('name', { length: 191 }),
    lastname: varchar('lastname', { length: 191 }),
    companyId: varchar('companyId', { length: 191 }).notNull(),
    email: varchar('email', { length: 191 }),
    emailVerified: datetime('emailVerified', { mode: 'string', fsp: 3 })
      .default(sql`CURRENT_TIMESTAMP(3)`)
      .notNull(),
    image: varchar('image', { length: 191 }),
    permissions: json('permissions').$type<{ boats?: 0 | 1 | 2; gps?: 0 | 1 | 2; reserves?: 0 | 1 | 2; company?: 0 | 1 | 2; partners?: 0 | 1 | 2; finances?: 0 | 1 | 2; spectate?: 0 | 1 | 2 ; elliot?: 0 | 1 | 2}>(),
    boats: json('boats').$type<number[]>(),
    captainDetails: json('captainDetails').$type<{ licence: string; prices: { duration: number; price: number }[]; comment: string }>(),
    phone: varchar('phone', { length: 191 }),
    settings: json('settings')
      .$type<EcUserSettings>()
      .default({ ui: { theme: 'dark' }, gps: { units: 'metric', geofences: true, insidePort: true, colors: 'boat', measurments: true, boatLabels: true } }),
    lastLogin: timestamp('lastLogin'),
  },
  (table) => {
    return {
      userId: primaryKey({columns: [table.id], name: 'PANEL_User_id'}),
      userEmailKey: unique('User_email_key').on(table.email),
    }
  }
)

export const userConfig = relations(ecuser, ({ one, many }) => ({
  company: one(company, { fields: [ecuser.companyId], references: [company.id] }),
  reserves: many(reserve),
}))

export type EcUserSettings = {
  ui: {
    theme: 'dark' | 'light'
  }
  gps: {
    units: 'metric' | 'imperial' | 'nautical'
    geofences: boolean
    insidePort: boolean
    colors: 'boat' | 'direction' | 'geofences'
    measurments: boolean
    boatLabels: boolean
  }
}

export type User = InferSelectModel<typeof ecuser>
// #endregion User
// #region VerificationToken
export const ecverificationToken = mysqlTable(
  'VerificationToken',
  {
    identifier: varchar('identifier', { length: 191 }).notNull(),
    token: varchar('token', { length: 191 }).notNull(),
    expires: datetime('expires', { mode: 'string', fsp: 3 }).notNull(),
  },
  (table) => {
    return {
      verificationTokenTokenKey: unique('VerificationToken_token_key').on(table.token),
      verificationTokenIdentifierTokenKey: unique('VerificationToken_identifier_token_key').on(table.identifier, table.token),
    }
  }
)
// #endregion VerificationToken
// #region Client
export const client = mysqlTable(
  'Client',
  {
    id: bigint('id', { mode: 'number' }).notNull().unique().autoincrement(),
    lastname: text('lastname'),
    name: text('name').notNull(),
    companyId: text('companyId').default('0').notNull(),
    email: text('email'),
    phone: text('phone'),
    photo: text('photo'),
    licence: text('licence'),
    licenceNumber: text('licenceNumber'),
    licenceName: text('licenceName'),
    licenceCountry: text('licenceCountry'),
    licencePhoto: text('licencePhoto'),
    licenceVerified: boolean('licenceVerified').default(true).notNull(),
    comment: text('comment'),
    docType: text('docType'),
    document: text('document'),
    documentPhoto: text('documentPhoto'),
    updatedAt: timestamp('updatedAt')
      .default(sql`now()`)
      .onUpdateNow()
      .notNull(),
  },
  (table) => {
    return {
      clientId: primaryKey({columns: [table.id], name: 'PANEL_Client_id'}),
    }
  }
)

export const clientConfig = relations(client, ({ one, many }) => ({
  company: one(company, { fields: [client.companyId], references: [company.id] }),
  reserves: many(reserve),
}))

export type Client = InferSelectModel<typeof client>
// #endregion Client
// #region Reseller
export const reseller = mysqlTable(
  'Reseller',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').notNull(),
    email: text('email'),
    phone: text('phone'),
    website: text('website'),
    comment: text('comment'),
    duty: int('duty').default(0).notNull(),
    discount: int('discount').default(0).notNull(),
    format: int('format').default(0).notNull(),
    companyId: text('companyId').default('0').notNull(),
    methodId: int('methodId'),
  },
  (table) => {
    return {
      resellerId: primaryKey({columns: [table.id], name: 'PANEL_Reseller_id'}),
    }
  }
)

export const resellerConfig = relations(reseller, ({ one, many }) => ({
  company: one(company, { fields: [reseller.companyId], references: [company.id] }),
  method: one(method, { fields: [reseller.methodId], references: [method.id] }),
  reserves: many(reserve),
}))
// #endregion Reseller
// #region Method
export const method = mysqlTable(
  'Method',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').notNull(),
    companyId: text('companyId').default('0').notNull(),
    description: text('description'),
    type: int('type').default(0).notNull(),
    use: json('use').$type<{ expenses: boolean; payments: boolean; resellers: boolean }>(),
  },
  (table) => {
    return {
      methodId: primaryKey({columns: [table.id], name: 'PANEL_Method_id'}),
    }
  }
)

export const methodConfig = relations(method, ({ one, many }) => ({
  company: one(company, { fields: [method.companyId], references: [company.id] }),
  finances: many(finance),
  resellers: many(reseller),
}))

export type Method = InferSelectModel<typeof method>
// #endregion Method
// #region Task

// #region TaskTypes
export enum TASK_STATUS {
  PENDING = 'PENDING',
  DONE = 'DONE',
  DELETED = 'DELETED',
}

export enum TASK_TYPE {
  UNIQUE = 'UNIQUE',
  RECURRING = 'RECURRING',
}

export enum TASK_PRIORITY {
  LOW = '0LOW',
  MEDIUM = '1MEDIUM',
  HIGH = '2HIGH',
}

export enum TASK_RELATED {
  BOAT = 'BOAT',
  EXTRA = 'EXTRA',
  SERVICE = 'SERVICE',
  EMPLOYEE = 'EMPLOYEE',
  RESERVE = 'RESERVE',
  COMPANY = 'COMPANY',
  NONE = 'NONE',
}

export type TASK_BODY = {
  description: string
  images: string[]
}

export type SUBTASK = {
  title: string
  pending: boolean
}

export type TASK_BUDGET = {
  amount: number
  createdAt: string
}

export type TASK_SHOP_ITEM = {
  name: string
  price: number
  store: string
  url: string
  pending: boolean
}
// #endregion TaskTypes

export const task = mysqlTable(
  'Task',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').default('0').notNull(),
    status: mysqlEnum('status', [TASK_STATUS.PENDING, ...Object.values(TASK_STATUS).filter((i) => i != TASK_STATUS.PENDING)])
      .default(TASK_STATUS.PENDING)
      .notNull(),
    type: mysqlEnum('type', [TASK_TYPE.UNIQUE, ...Object.values(TASK_TYPE).filter((i) => i != TASK_TYPE.UNIQUE)])
      .default(TASK_TYPE.UNIQUE)
      .notNull(),
    priority: mysqlEnum('priority', [TASK_PRIORITY.LOW, ...Object.values(TASK_PRIORITY).filter((i) => i != TASK_PRIORITY.LOW)])
      .default(TASK_PRIORITY.LOW)
      .notNull(),
    assignedTo: json('assignedTo').$type<string[]>().default([]).notNull(),
    title: text('title').notNull(),
    body: json('body').$type<TASK_BODY>().default({ description: '', images: [] }).notNull(),
    budget: json('budget').$type<TASK_BUDGET[]>().default([]).notNull(),
    subTasks: json('subTasks').$type<SUBTASK[]>().default([]).notNull(),
    createdAt: timestamp('createdAt')
      .default(sql`now()`)
      .notNull(),
    updatedAt: timestamp('updatedAt')
      .default(sql`now()`)
      .onUpdateNow()
      .notNull(),
    createdBy: text('createdBy').default('').notNull(),
    notifyBy: timestamp('notifyBy'),
    startDate: timestamp('startDate'),
    endDate: timestamp('endDate'),
    departmentId: int('departmentId'),
    boatId: int('boatId'),
    reserveId: int('reserveId'),
    serviceId: int('serviceId'),
    extraId: int('extraId'),
  },
  (table) => {
    return {
      taskId: primaryKey({columns: [table.id], name: 'PANEL_Task_id'}),
    }
  }
)

export const taskConfig = relations(task, ({ many, one }) => ({
  child: many(taskToTasks, { relationName: 'children' }),
  parent: many(taskToTasks, { relationName: 'parent' }),
  finances: many(finance),
  comments: many(taskComment),
  createdByUser: one(ecuser, { fields: [task.createdBy], references: [ecuser.id] }),
  department: one(department, { fields: [task.departmentId], references: [department.id] }),
  boat: one(boat, { fields: [task.boatId], references: [boat.id] }),
  reserve: one(reserve, { fields: [task.reserveId], references: [reserve.id] }),
  service: one(service, { fields: [task.serviceId], references: [service.id] }),
  extra: one(extra, { fields: [task.extraId], references: [extra.id] }),
  shopList: one(shopList),
}))

export const updateTaskSchema = createInsertSchema(task, {
  id: z.number(),
  assignedTo: z.array(z.string()).optional(),
  body: z.object({ description: z.string(), images: z.array(z.string()).optional().default([]) }),
  budget: z.array(z.object({ amount: z.number(), createdAt: z.string() })),
  subTasks: z.array(z.object({ title: z.string(), pending: z.boolean() })),
})

interface TaskFinance extends InferSelectModel<typeof finance> {
  department: InferSelectModel<typeof department> | null
  method: InferSelectModel<typeof method> | null
  categoryRelated: InferSelectModel<typeof financeCategory> | null
}

interface TaskComments extends InferSelectModel<typeof taskComment> {
  user: { id: string; name: string | null; lastname: string | null } | null
}

export type Task = InferSelectModel<typeof task> & {
  department: InferSelectModel<typeof department> | null
  finances: InferSelectModel<typeof finance>[]
  comments: InferSelectModel<typeof taskComment>[]
  boat: { name: string } | null
  extra: { name: string } | null
  service: { name: string } | null
  shopList: InferSelectModel<typeof shopList> | null
}

export type FullTask = InferSelectModel<typeof task> & {
  department: InferSelectModel<typeof department> | null
  finances: TaskFinance[]
  comments: TaskComments[]
  boat: InferSelectModel<typeof boat> | null
  extra: InferSelectModel<typeof extra> | null
  service: InferSelectModel<typeof service> | null
  reserve: InferSelectModel<typeof reserve> | null
  shopList: InferSelectModel<typeof shopList> | null
}

// #endregion Task
// #region TaskComment
export const taskComment = mysqlTable(
  'TaskComment',
  {
    id: int('id').notNull().unique().autoincrement(),
    taskId: int('taskId').notNull(),
    body: text('body').notNull(),
    createdAt: timestamp('createdAt')
      .default(sql`now()`)
      .notNull(),
    createdBy: text('createdBy').default('').notNull(),
  },
  (table) => {
    return {
      taskCommentId: primaryKey({columns: [table.id], name: 'PANEL_TaskComment_id'}),
    }
  }
)

export const taskCommentConfig = relations(taskComment, ({ one }) => ({
  task: one(task, { fields: [taskComment.taskId], references: [task.id] }),
  user: one(ecuser, { fields: [taskComment.createdBy], references: [ecuser.id] }),
}))

export type TaskComment = InferSelectModel<typeof taskComment> & {
  user?: { id: string; name: string; lastname: string }
}
// #endregion TaskComment
// #region TaskToTasks
export const taskToTasks = mysqlTable(
  'TaskToTasks',
  {
    parentId: int('parentId').notNull(),
    childId: int('childId').notNull(),
  },
  (table) => {
    return {
      child: primaryKey(table.childId),
    }
  }
)

export const taskToTasksRelations = relations(taskToTasks, ({ one }) => ({
  child: one(task, { fields: [taskToTasks.childId], references: [task.id], relationName: 'parent' }),
  parent: one(task, { fields: [taskToTasks.parentId], references: [task.id], relationName: 'children' }),
}))
// #endregion TaskToTasks
// #region Chat
// #region ChatTypes
export enum CHAT_STATUS {
  SELECT_STATUS = 'SELECT_STATUS',
  WAITING_FOR_PAY = 'WAITING_FOR_PAY',
  WAITING_FOR_ASSISTANT = 'WAITING_FOR_ASSISTANT',
  CONCLUDE_CHAT = 'CONCLUDE_CHAT',
}

export enum CHAT_TOPIC {
  IDLE = 'IDLE',
  BOOKING = 'BOOKING',
  INFO = 'INFO',
  CLAIM = 'CLAIM',
}
// #endregion ChatTypes
export const chat = mysqlTable(
  'Chat',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').default('0').notNull(),
    detail: text('detail'),
    pinned: boolean('pinned').default(false).notNull(),
    status: mysqlEnum('status', [CHAT_STATUS.SELECT_STATUS, ...Object.values(CHAT_STATUS).filter((i) => i != CHAT_STATUS.SELECT_STATUS)])
      .default(CHAT_STATUS.SELECT_STATUS)
      .notNull(),
    topic: mysqlEnum('topic', [CHAT_TOPIC.IDLE, ...Object.values(CHAT_TOPIC).filter((i) => i != CHAT_TOPIC.IDLE)])
      .default(CHAT_TOPIC.IDLE)
      .notNull(),
    createdAt: timestamp('createdAt')
      .default(sql`now()`)
      .notNull(),
    updatedAt: timestamp('updatedAt')
      .default(sql`now()`)
      .onUpdateNow()
      .notNull(),
    clientId: bigint('clientId', { mode: 'number' }).notNull(),
    assignTo: text('assignTo'),
  },
  (table) => {
    return {
      chatId: primaryKey({columns: [table.id], name: 'PANEL_Chat_id'}),
    }
  }
)

export const chatConfig = relations(chat, ({ one, many }) => ({
  messages: many(chatMessage),
  client: one(client, { fields: [chat.clientId], references: [client.id] }),
  assignedUser: one(ecuser, { fields: [chat.assignTo], references: [ecuser.id] }),
}))

export type Chat = InferSelectModel<typeof chat> & {
  messages: InferSelectModel<typeof chatMessage>[]
  client: InferSelectModel<typeof client>
}
// #endregion Chat
// #region ChatMessage
export const chatMessage = mysqlTable(
  'ChatMessage',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').default('0').notNull(),
    content: text('content').notNull(),
    createdAt: timestamp('createdAt')
      .default(sql`now()`)
      .notNull(),
    userId: text('userId'),
    clientId: bigint('clientId', { mode: 'number' }),
    chatId: int('chatId').notNull(),
  },
  (table) => {
    return {
      chatId: primaryKey({columns: [table.id], name: 'PANEL_ChatMessage_id'}),
    }
  }
)

export const chatMessageConfig = relations(chatMessage, ({ one }) => ({
  chat: one(chat, { fields: [chatMessage.chatId], references: [chat.id] }),
  client: one(client, { fields: [chatMessage.clientId], references: [client.id] }),
  user: one(ecuser, { fields: [chatMessage.userId], references: [ecuser.id] }),
}))

export type ChatMessage = InferSelectModel<typeof chatMessage>
// #endregion ChatMessage
// #region PartnerComment
export const partnerComment = mysqlTable(
  'PartnerComment',
  {
    id: int('id').notNull().unique().autoincrement(),
    partnerId: varchar('partnerId', { length: 191 }).notNull(),
    content: text('content').notNull(),
    createdAt: timestamp('createdAt')
      .default(sql`now()`)
      .notNull(),
    createdBy: text('createdBy').default('').notNull(),
  },
  (table) => {
    return {
      chatId: primaryKey({columns: [table.id], name: 'PANEL_PartnerComment_id'}),
    }
  }
)

export const partnerCommentConfig = relations(partnerComment, ({ one }) => ({
  partner: one(partner, { fields: [partnerComment.partnerId], references: [partner.id] }),
  user: one(ecuser, { fields: [partnerComment.createdBy], references: [ecuser.id] }),
}))
// #endregion PartnerComment
// #region Department
export const department = mysqlTable(
  'Department',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').notNull(),
    companyId: text('companyId').default('0').notNull(),
    status: boolean('status').default(true).notNull(),
  },
  (table) => {
    return {
      DepartmentId: primaryKey({columns: [table.id], name: 'PANEL_Department_id'}),
    }
  }
)

export const departmentConfig = relations(department, ({ one }) => ({
  company: one(company, { fields: [department.companyId], references: [company.id] }),
}))
// #endregion Department
// #region FinanceCategory
export const financeCategory = mysqlTable(
  'FinanceCategory',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').notNull(),
    name: text('category'),
    description: text('description'),
    status: boolean('status').default(true).notNull(),
  },
  (table) => {
    return {
      financesId: primaryKey({columns: [table.id], name: 'PANEL_FinanceCategory_id'}),
    }
  }
)

export const financeCategoryConfig = relations(financeCategory, ({ one, many }) => ({
  finances: many(finance),
  company: one(company, { fields: [financeCategory.companyId], references: [company.id] }),
}))
// #endregion FinanceCategory
// #region ShopList

export type SHOPLIST_ITEM = {
  name: string
  price: number
  store: string
  url: string
  pending: boolean
  image?: string
}
export enum SHOPLIST_STATUS {
  PENDING = 'PENDING',
  DONE = 'DONE',
  DELETED = 'DELETED',
}

export const shopList = mysqlTable(
  'ShopList',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').default('0').notNull(),
    status: mysqlEnum('status', [SHOPLIST_STATUS.PENDING, ...Object.values(SHOPLIST_STATUS).filter((i) => i != SHOPLIST_STATUS.PENDING)])
      .default(SHOPLIST_STATUS.PENDING)
      .notNull(),
    title: text('title').notNull(),
    description: text('description'),
    items: json('items').$type<SHOPLIST_ITEM[]>().default([]).notNull(),
    createdAt: timestamp('createdAt')
      .default(sql`now()`)
      .notNull(),
    updatedAt: timestamp('updatedAt')
      .default(sql`now()`)
      .onUpdateNow()
      .notNull(),
    departmentId: int('departmentId'),
    boatId: int('boatId'),
    reserveId: int('reserveId'),
    serviceId: int('serviceId'),
    extraId: int('extraId'),
    taskId: int('taskId'),
  },
  (table) => {
    return {
      shopListId: primaryKey({columns: [table.id], name: 'PANEL_ShopList_id'}),
    }
  }
)

export const shopListConfig = relations(shopList, ({ one }) => ({
  department: one(department, { fields: [shopList.departmentId], references: [department.id] }),
  boat: one(boat, { fields: [shopList.boatId], references: [boat.id] }),
  reserve: one(reserve, { fields: [shopList.reserveId], references: [reserve.id] }),
  service: one(service, { fields: [shopList.serviceId], references: [service.id] }),
  extra: one(extra, { fields: [shopList.extraId], references: [extra.id] }),
  task: one(task, { fields: [shopList.taskId], references: [task.id] }),
}))

export const shopItem = mysqlTable(
  'ShopItem',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').default('0').notNull(),
    name: text('name').notNull(),
    price: int('price'),
    store: text('store'),
    url: text('url'),
    description: text('description'),
    pending: boolean('pending').default(true).notNull(),
    image: text('image'),
    departmentId: int('departmentId'),
    boatId: int('boatId'),
    reserveId: int('reserveId'),
    serviceId: int('serviceId'),
    extraId: int('extraId'),
    taskId: int('taskId'),
  },
  (table) => {
    return {
      shopItemId: primaryKey({columns: [table.id], name: 'PANEL_ShopItem_id'}),
    }
  }
)

export const shopItemConfig = relations(shopItem, ({ one }) => ({
  department: one(department, { fields: [shopItem.departmentId], references: [department.id] }),
  boat: one(boat, { fields: [shopItem.boatId], references: [boat.id] }),
  reserve: one(reserve, { fields: [shopItem.reserveId], references: [reserve.id] }),
  service: one(service, { fields: [shopItem.serviceId], references: [service.id] }),
  extra: one(extra, { fields: [shopItem.extraId], references: [extra.id] }),
  task: one(task, { fields: [shopItem.taskId], references: [task.id] }),
}))

export const shopItemSchema = createInsertSchema(shopItem)
export const updateShopItemSchema = createInsertSchema(shopItem, {
  id: z.number(),
})
export const updateShopListSchema = createInsertSchema(shopList, {
  id: z.number(),
  items: z.array(z.object({ name: z.string(), price: z.number().default(0), store: z.string(), url: z.string(), pending: z.boolean(), image: z.string().optional() })),
})

export type ShopItem = InferSelectModel<typeof shopItem> 

export type SimpleShopList = InferSelectModel<typeof shopList>

export type ShopList = InferSelectModel<typeof shopList> & {
  department: InferSelectModel<typeof department> | null
  boat: { id: number; name: string } | null
  extra: { id: number; name: string } | null
  service: { id: number; name: string } | null
  reserve: InferSelectModel<typeof reserve> | null
  task: { id: number; title: string } | null
}

export type FullShopList = InferSelectModel<typeof shopList> & {
  department: InferSelectModel<typeof department> | null
  boat: InferSelectModel<typeof boat> | null
  extra: InferSelectModel<typeof extra> | null
  service: InferSelectModel<typeof service> | null
  reserve: InferSelectModel<typeof reserve> | null
  task: InferSelectModel<typeof task> | null
}
// #endregion ShopList
// #region SearchResults

export const searchResults = mysqlTable(
  'SearchResults',
  {
    id: int('id').notNull().unique().autoincrement(),
    companyId: text('companyId').default('0').notNull(),
    title: text('title').default('Titulo').notNull(),
    description: text('description').default('Descripcion').notNull(),
    url: text('url').default('URL').notNull(),
    favicons: json('favicons').$type<{ high_res: string; low_res: string }>().default({ high_res: '', low_res: '' }).notNull(),
    status: text('status').default('PENDING').notNull(),
    query: text('query').default('').notNull(),
  },
  (table) => {
    return {
      searchResultsId: primaryKey({columns: [table.id], name: 'PANEL_SearchResults_id'}),
    }
  }
)
// #endregion SearchResults
// #region Origin

export enum ORIGIN_TYPE {
  RESELLER = 'RESELLER',
  DIRECT = 'DIRECT',
}

export const origin = mysqlTable(
  'Origin',
  {
    id: int('id').notNull().unique().autoincrement(),
    type: mysqlEnum('type', [ORIGIN_TYPE.DIRECT, ...Object.values(ORIGIN_TYPE).filter((i) => i != ORIGIN_TYPE.DIRECT)])
      .default(ORIGIN_TYPE.DIRECT)
      .notNull(),
    name: text('name').notNull(),
    email: text('email'),
    phone: text('phone'),
    website: text('website'),
    detail: text('detail'),
    duty: float('duty'),
    discount: int('discount'),
    charges: float('charges'),
    chargesSign: text('chargesSign'),
    includesTaxes: boolean('includesTaxes').default(false).notNull(),
    companyId: text('companyId').default('0').notNull(),
    methodId: int('methodId'),
  },
  (table) => {
    return {
      originId: primaryKey({columns: [table.id], name: 'PANEL_Origin_id'}),
    }
  }
)

export const originConfig = relations(origin, ({ one }) => ({
  company: one(company, { fields: [origin.companyId], references: [company.id] }),
  method: one(method, { fields: [origin.methodId], references: [method.id] }),
}))

export type Origin = InferSelectModel<typeof origin>
// #endregion Origin
// #region Suscriber
export const suscribers = mysqlTable(
  'Suscribers',
  {
    id: int('id').notNull().unique().autoincrement(),
    email: text('email'),
    createdAt: timestamp('createdAt')
      .default(sql`now()`)
      .notNull(),
  },
  (table) => {
    return {
      suscriberId: primaryKey({columns: [table.id], name: 'PANEL_Suscribers_id'}),
    }
  }
)
// #endregion Suscriber
// #region Discount

export const discount = mysqlTable(
  'Discount',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').notNull(),
    from: date('from'),
    to: date('to'),
    percent: int('percent'),
    amount: int('amount'),
    code: text('code'),
    visible: boolean('visible').default(false).notNull(),
    companyId: text('companyId').default('0').notNull(),
    methodId: int('methodId'),
  },
  (table) => {
    return {
      discountId: primaryKey({columns: [table.id], name: 'PANEL_Discount_id'}),
    }
  }
)

export const discountConfig = relations(discount, ({ one, many }) => ({
  company: one(company, { fields: [discount.companyId], references: [company.id] }),
  method: one(method, { fields: [discount.methodId], references: [method.id] }),
  boats: many(discountToBoat),
}))

export type Discount = InferSelectModel<typeof discount>
// #endregion Discount
// #region DiscountToBoat
export const discountToBoat = mysqlTable(
  'DiscountToBoat',
  {
    discountId: int('discountId').notNull(),
    boatId: int('boatId').notNull(),
  },
  (table) => {
    return {
      child: primaryKey(table.boatId),
    }
  }
)

export const discountToBoatRelations = relations(discountToBoat, ({ one }) => ({
  boat: one(boat, { fields: [discountToBoat.boatId], references: [boat.id], relationName: 'discount' }),
  discount: one(discount, { fields: [discountToBoat.discountId], references: [discount.id], relationName: 'boat' }),
}))
// #endregion DiscountToBoat
// #region Places

export const place = mysqlTable(
  'Place',
  {
    id: int('id').notNull().unique().autoincrement(),
    name: text('name').notNull(),
    position: json('position').$type<{ lat: number; lng: number }>().notNull(),
    map: text('map'),
    photos: json('photos').$type<string[]>().default([]).notNull(),
    priority: int('priority').default(0).notNull(),
  },
  (table) => {
    return {
      placeId: primaryKey({columns: [table.id], name: 'PANEL_Place_id'}),
    }
  }
)

export const placeConfig = relations(place, ({ many }) => ({
  boats: many(boat),
}))
// #endregion Places

// #endregion EASYCHARTER

// #region User

export type UserSettings = {
  ui: {
    theme: 'dark' | 'light'
  }
}

export const user = ECMysqlTable(
  'User',
  {
    id: varchar('id', { length: 191 })
      .notNull()
      .primaryKey()
      .default(sql`(uuid())`),
    code: varchar('code', { length: 191 }),
    name: varchar('name', { length: 191 }),
    lastname: varchar('lastname', { length: 191 }),
    email: varchar('email', { length: 191 }),
    emailVerified: datetime('emailVerified', { mode: 'string', fsp: 3 })
      .default(sql`CURRENT_TIMESTAMP(3)`)
      .notNull(),
    image: varchar('image', { length: 191 }),
    phone: varchar('phone', { length: 191 }),
    docType: text('docType'),
    document: text('document'),
    birthdate: datetime('birthdate', { fsp: 3 }),
    settings: json('settings')
      .$type<UserSettings>()
      .default({ ui: { theme: 'dark' } }),
    reseller: boolean('reseller').default(false).notNull(),
    address: text('address'),
    placeId: text('placeId'),
    lastLogin: timestamp('lastLogin'),
  },
  (table) => {
    return {
      userId: primaryKey({ columns: [table.id], name: 'EC_User_id' }),
      userEmailKey: unique('User_email_key').on(table.email),
      userCodeKey: unique('User_code_key').on(table.code),
    }
  }
)

// #endregion User
// #region VerificationToken
export const verificationToken = ECMysqlTable(
  'VerificationToken',
  {
    identifier: varchar('identifier', { length: 191 }).notNull(),
    token: varchar('token', { length: 191 }).notNull(),
    expires: datetime('expires', { mode: 'string', fsp: 3 }).notNull(),
  },
  (table) => {
    return {
      verificationTokenTokenKey: unique('VerificationToken_token_key').on(table.token),
      verificationTokenIdentifierTokenKey: unique('VerificationToken_identifier_token_key').on(table.identifier, table.token),
    }
  }
)
// #endregion VerificationToken
// #region Session
export const session = ECMysqlTable(
  'Session',
  {
    id: varchar('id', { length: 191 })
      .notNull()
      .unique()
      .default(sql`(uuid())`),
    sessionToken: varchar('sessionToken', { length: 191 }).notNull(),
    userId: varchar('userId', { length: 191 }).notNull(),
    expires: datetime('expires', { mode: 'string', fsp: 3 }).notNull(),
  },
  (table) => {
    return {
      sessionId: primaryKey({ columns: [table.id], name: 'EC_Session_id' }),
      sessionSessionTokenKey: unique('Session_sessionToken_key').on(table.sessionToken),
    }
  }
)

// #endregion Session
// #region Account
export const account = ECMysqlTable(
  'Account',
  {
    id: varchar('id', { length: 191 })
      .notNull()
      .default(sql`(uuid())`),
    userId: varchar('userId', { length: 191 }).notNull(),
    type: varchar('type', { length: 191 }).notNull(),
    provider: varchar('provider', { length: 191 }).notNull(),
    providerAccountId: varchar('providerAccountId', { length: 191 }).notNull(),
    refreshToken: varchar('refresh_token', { length: 191 }),
    accessToken: varchar('access_token', { length: 191 }),
    expiresAt: int('expires_at'),
    tokenType: varchar('token_type', { length: 191 }),
    scope: varchar('scope', { length: 191 }),
    idToken: varchar('id_token', { length: 191 }),
    sessionState: varchar('session_state', { length: 191 }),
  },
  (table) => {
    return {
      accountId: primaryKey({ columns: [table.id], name: 'EC_Account_id' }),
      accountProviderProviderAccountIdKey: unique('Account_provider_providerAccountId_key').on(table.provider, table.providerAccountId),
    }
  }
)
// #endregion Account
PurpleTape commented 5 months ago

@AlexBlokh

Hello!

In my case, the error occurs during the following action:

  1. The database is empty, the table does not exist

    > drizzle-kit push
  2. The table was created without errors, run the command again

    
    > drizzle-kit push

drizzle-kit: v0.21.4 drizzle-orm: v0.30.10

No config path provided, using default path Reading config file '/app/drizzle.config.ts' Using 'pg' driver for database querying [✓] Pulling schema from database...TypeError: Cannot use 'in' operator to search for 'default' in undefined at /app/node_modules/drizzle-kit/bin.cjs:18362:23 at Array.map () at alternationsInColumn (/app/node_modules/drizzle-kit/bin.cjs:18361:10) at /app/node_modules/drizzle-kit/bin.cjs:18321:49 at Array.map () at findAlternationsInTable (/app/node_modules/drizzle-kit/bin.cjs:18321:37) at /app/node_modules/drizzle-kit/bin.cjs:18203:14 at Array.map () at applyJsonDiff (/app/node_modules/drizzle-kit/bin.cjs:18201:69) at applyPgSnapshotsDiff (/app/node_modules/drizzle-kit/bin.cjs:19822:26)


My scheme:
```ts
import { pgTable, serial, timestamp } from 'drizzle-orm/pg-core';

export const test = pgTable('test', {
    id: serial('id').primaryKey().notNull(),
    deactivatedDate: timestamp('deactivated_date', {
        mode: 'date',
        precision: 0,
    }),
});

Config:

import { defineConfig } from 'drizzle-kit';

export default defineConfig({
    dialect: 'postgresql',
    out: './migrations',
    schema: [
        './drizzle-test/*.schema.ts',
    ],
    dbCredentials: {
        host: 'localhost',
        port: 5432,
        user: 'user',
        password: 'password',
        database: 'drizzle-test',
    },
    verbose: true,
    strict: true,
});
PurpleTape commented 5 months ago

Also, when I remove the deactivated_date column, there is no error when running the command

dankgarlic1 commented 1 month ago

Also, when I remove the deactivated_date column, there is no error when running the command

I am also facing the same issue, the problem is with timestamp, any solutions?