Remove Odoo order fallback
All checks were successful
Build Docker Image / build (push) Successful in 5m6s

This commit is contained in:
Ruslan Bakiev
2026-05-31 12:05:54 +05:00
parent e8051260db
commit e028f7c038

View File

@@ -9,8 +9,6 @@ import {
import { requireScopes, type AuthContext } from '../auth.js' import { requireScopes, type AuthContext } from '../auth.js'
import { prisma } from '../db.js' import { prisma } from '../db.js'
const ODOO_INTERNAL_URL = process.env.ODOO_INTERNAL_URL || 'odoo:8069'
const quotationInclude = { const quotationInclude = {
selectedTariff: true, selectedTariff: true,
changes: { changes: {
@@ -27,82 +25,6 @@ const orderInclude = {
type QuotationWithRelations = Prisma.QuotationGetPayload<{ include: typeof quotationInclude }> type QuotationWithRelations = Prisma.QuotationGetPayload<{ include: typeof quotationInclude }>
type OrderWithRelations = Prisma.OrderGetPayload<{ include: typeof orderInclude }> type OrderWithRelations = Prisma.OrderGetPayload<{ include: typeof orderInclude }>
interface OdooCompany {
uuid?: string
name?: string
tax_id?: string
country?: string
country_code?: string
active?: boolean
}
interface OdooTrip {
uuid?: string
name?: string
sequence?: number
company?: OdooCompany
planned_loading_date?: string
actual_loading_date?: string
real_loading_date?: string
planned_unloading_date?: string
actual_unloading_date?: string
planned_weight?: number
weight_at_loading?: number
weight_at_unloading?: number
}
interface OdooStage {
uuid?: string
name?: string
sequence?: number
stage_type?: string
transport_type?: string
source_location_name?: string
source_latitude?: number
source_longitude?: number
destination_location_name?: string
destination_latitude?: number
destination_longitude?: number
location_name?: string
location_latitude?: number
location_longitude?: number
selected_company?: OdooCompany
trips?: OdooTrip[]
}
interface OdooOrderLine {
uuid?: string
product_uuid?: string
product_name?: string
quantity?: number
unit?: string
price_unit?: number
subtotal?: number
currency?: string
notes?: string
}
interface OdooOrder {
uuid?: string
name?: string
team_uuid?: string
user_id?: string
status?: string
total_amount?: number
currency?: string
source_location_uuid?: string
source_location_name?: string
source_latitude?: number
source_longitude?: number
destination_location_uuid?: string
destination_location_name?: string
created_at?: string
updated_at?: string
notes?: string
order_lines?: OdooOrderLine[]
stages?: OdooStage[]
}
interface TariffMatchResult { interface TariffMatchResult {
tariff: PrismaTariffReference tariff: PrismaTariffReference
score: number score: number
@@ -491,95 +413,6 @@ function assertTeamAccess(ctx: AuthContext): { teamUuid: string; userId: string
} }
} }
function mapCompany(c?: OdooCompany | null) {
if (!c) return null
return {
uuid: c.uuid ?? '',
name: c.name ?? '',
taxId: c.tax_id ?? '',
country: c.country ?? '',
countryCode: c.country_code ?? '',
active: c.active ?? true,
}
}
function mapTrip(t: OdooTrip) {
return {
uuid: t.uuid,
name: t.name,
sequence: t.sequence,
company: mapCompany(t.company),
plannedLoadingDate: t.planned_loading_date,
actualLoadingDate: t.actual_loading_date,
realLoadingDate: t.real_loading_date,
plannedUnloadingDate: t.planned_unloading_date,
actualUnloadingDate: t.actual_unloading_date,
plannedWeight: t.planned_weight,
weightAtLoading: t.weight_at_loading,
weightAtUnloading: t.weight_at_unloading,
}
}
function mapStage(s: OdooStage) {
return {
uuid: s.uuid,
name: s.name,
sequence: s.sequence,
stageType: s.stage_type,
transportType: s.transport_type,
sourceLocationName: s.source_location_name,
sourceLatitude: s.source_latitude,
sourceLongitude: s.source_longitude,
destinationLocationName: s.destination_location_name,
destinationLatitude: s.destination_latitude,
destinationLongitude: s.destination_longitude,
locationName: s.location_name,
locationLatitude: s.location_latitude,
locationLongitude: s.location_longitude,
selectedCompany: mapCompany(s.selected_company),
trips: (s.trips ?? []).map(mapTrip),
}
}
function mapOdooOrder(o: OdooOrder) {
const stages = o.stages ?? []
const firstStage = stages[0]
return {
uuid: o.uuid,
quotationUuid: null,
name: o.name,
teamUuid: o.team_uuid,
userId: o.user_id,
status: o.status ?? 'active',
totalAmount: o.total_amount,
currency: o.currency,
sourceCountryCode: null,
sourceLocationUuid: o.source_location_uuid,
sourceLocationName: o.source_location_name,
sourceLatitude: o.source_latitude || firstStage?.source_latitude,
sourceLongitude: o.source_longitude || firstStage?.source_longitude,
destinationCountryCode: null,
destinationLocationUuid: o.destination_location_uuid,
destinationLocationName: o.destination_location_name,
etaDays: null,
createdAt: o.created_at,
updatedAt: o.updated_at,
notes: o.notes ?? '',
orderLines: (o.order_lines ?? []).map(l => ({
uuid: l.uuid,
productUuid: l.product_uuid,
productName: l.product_name,
quantity: l.quantity,
unit: l.unit,
priceUnit: l.price_unit,
subtotal: l.subtotal,
currency: l.currency,
notes: l.notes ?? '',
})),
stages: stages.map(mapStage),
}
}
function mapTariffReference(item: PrismaTariffReference) { function mapTariffReference(item: PrismaTariffReference) {
return { return {
uuid: item.uuid, uuid: item.uuid,
@@ -693,53 +526,6 @@ function mapLocalOrder(item: OrderWithRelations) {
} }
} }
async function fetchOdoo(path: string): Promise<Response> {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 10000)
try {
return await fetch(`http://${ODOO_INTERNAL_URL}${path}`, { signal: controller.signal })
} finally {
clearTimeout(timeout)
}
}
async function fetchTeamOdooOrders(teamUuid: string) {
try {
const res = await fetchOdoo(`/fastapi/orders/orders/team/${teamUuid}`)
if (!res.ok) return []
const data = (await res.json()) as OdooOrder[]
return data.map(mapOdooOrder)
} catch (error) {
console.error('Error calling Odoo:', error)
return []
}
}
async function fetchSingleOdooOrder(orderUuid: string, teamUuid: string) {
try {
const res = await fetchOdoo(`/fastapi/orders/orders/${orderUuid}`)
if (!res.ok) return null
const data = (await res.json()) as OdooOrder
if (data.team_uuid !== teamUuid) {
throw new GraphQLError('Access denied: order belongs to different team')
}
return mapOdooOrder(data)
} catch (error) {
if (error instanceof GraphQLError) throw error
console.error('Error calling Odoo:', error)
return null
}
}
function compareByCreatedAtDesc(
left: { createdAt: string | null | undefined },
right: { createdAt: string | null | undefined },
) {
const leftTime = left.createdAt ? new Date(left.createdAt).getTime() : 0
const rightTime = right.createdAt ? new Date(right.createdAt).getTime() : 0
return rightTime - leftTime
}
function requiredString(value: unknown, label: string): string { function requiredString(value: unknown, label: string): string {
const next = normalizeString(value) const next = normalizeString(value)
if (!next) { if (!next) {
@@ -1098,26 +884,23 @@ export const teamResolvers = {
getTeamOrders: async (_: unknown, __: unknown, ctx: AuthContext) => { getTeamOrders: async (_: unknown, __: unknown, ctx: AuthContext) => {
const { teamUuid } = assertTeamAccess(ctx) const { teamUuid } = assertTeamAccess(ctx)
const [localOrders, odooOrders] = await Promise.all([ const localOrders = await prisma.order.findMany({
prisma.order.findMany({ where: {
where: { teamUuid,
teamUuid, },
}, include: orderInclude,
include: orderInclude, orderBy: {
orderBy: { createdAt: 'desc',
createdAt: 'desc', },
}, })
}),
fetchTeamOdooOrders(teamUuid),
])
return [...localOrders.map(mapLocalOrder), ...odooOrders].sort(compareByCreatedAtDesc) return localOrders.map(mapLocalOrder)
}, },
getOrder: async (_: unknown, args: { orderUuid: string }, ctx: AuthContext) => { getOrder: async (_: unknown, args: { orderUuid: string }, ctx: AuthContext) => {
const { teamUuid } = assertTeamAccess(ctx) const { teamUuid } = assertTeamAccess(ctx)
const localOrder = await prisma.order.findFirst({ const order = await prisma.order.findFirst({
where: { where: {
uuid: args.orderUuid, uuid: args.orderUuid,
teamUuid, teamUuid,
@@ -1125,11 +908,7 @@ export const teamResolvers = {
include: orderInclude, include: orderInclude,
}) })
if (localOrder) { return order ? mapLocalOrder(order) : null
return mapLocalOrder(localOrder)
}
return await fetchSingleOdooOrder(args.orderUuid, teamUuid)
}, },
quotations: async (_: unknown, args: { status?: string | null }, ctx: AuthContext) => { quotations: async (_: unknown, args: { status?: string | null }, ctx: AuthContext) => {