package de.stadtbote.votian; import java.math.BigDecimal; import java.sql.Timestamp; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Map; import static de.stadtbote.votian.Models.*; /** Mappings traced to include/mcglobal.inc.php and the corresponding PHP job/customer/courier modules. */ final class LegacyMapping { private LegacyMapping() {} static Object value(Map row,String key) { Object result = row.get(key); if (result == null) result = row.get(key.toUpperCase(java.util.Locale.ROOT)); return result; } static String text(Map row,String key) { Object v=value(row,key); return v==null ? "" : v.toString(); } static long id(Map row,String key) { Object v=value(row,key); if(v==null || v.toString().isBlank()) return 0; // JDBC may return Boolean for legacy TINYINT(1) flags, such as cmp_archived. if(v instanceof Boolean flag) return flag ? 1 : 0; return v instanceof Number n ? n.longValue() : Long.parseLong(v.toString()); } static Long nullableId(Map row,String key) { long n=id(row,key); return n>0?n:null; } static BigDecimal decimal(Map row,String key) { Object v=value(row,key); if(v==null || v.toString().isBlank()) return null; return v instanceof BigDecimal d ? d : new BigDecimal(v.toString()); } static LocalDateTime dateTime(Map row,String key) { Object v=value(row,key); if(v==null) return null; if(v instanceof Timestamp t) return t.toLocalDateTime(); if(v instanceof LocalDateTime t) return t; String s=v.toString(); if(s.isBlank() || s.startsWith("0000-")) return null; if(s.matches("\\d{14}")) return LocalDateTime.parse(s,DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); return LocalDateTime.parse(s.replace(' ','T')); } static String join(String a,String b) { return (a+" "+b).trim(); } static Customer customer(Map row) { Customer c=new Customer();c.costCenterId=nullableId(row,"app_costcenter_id");c.costCenterName=text(row,"app_costcenter_name");c.id=id(row,"cs_id");c.number=text(row,"cs_eid");c.name=text(row,"cmp_comp"); c.street=join(text(row,"ad_street"),text(row,"cmp_hsno"));c.postalCode=text(row,"ad_zipcode");c.city=text(row,"ad_city"); c.contact=join(text(row,"usr_firstname"),text(row,"usr_name"));c.phone=text(row,"usr_phone");c.email=text(row,"usr_email"); c.paymentTermLabel=text(row,"payment_term_label");String days=text(row,"cs_payment_term");c.paymentDays=days.matches("\\d+")?Integer.valueOf(days):null; c.active=id(row,"cmp_archived")==0 && id(row,"cmp_visible")==1 && id(row,"cmp_authenticated")==1; return c; } static Courier courier(Map row) { Courier c=new Courier();c.id=id(row,"cr_id");c.number=text(row,"cr_eid");c.name=text(row,"cmp_comp"); if(c.name.isBlank()) c.name=join(text(row,"usr_firstname"),text(row,"usr_name")); c.phone=text(row,"usr_phone");c.vehicle=text(row,"vehicle_label");c.plate=text(row,"crvh_vh_sign"); c.active=id(row,"cmp_archived")==0 && id(row,"cmp_visible")==1 && id(row,"cmp_authenticated")==1; c.status=!c.active?CourierStatus.OFFLINE:(id(row,"active_jobs")>0?CourierStatus.BUSY:(id(row,"cr_available")==1?CourierStatus.AVAILABLE:CourierStatus.OFFLINE)); return c; } static DeliveryOrder order(Map row) { DeliveryOrder o=new DeliveryOrder();o.id=id(row,"jb_id");o.number=Long.toString(o.id);o.version=id(row,"jb_version"); o.customerId=nullableId(row,"app_customer_id");o.customerName=text(row,"app_customer_name"); o.payerCustomerId=nullableId(row,"app_payer_customer_id");o.ordererCustomerId=nullableId(row,"app_orderer_customer_id");o.courierId=nullableId(row,"cr_id"); o.legacyStatus=(int)id(row,"jb_status");int storno=(int)id(row,"jb_storno");o.legacyStorno=storno;o.isCreditNote=storno==3||storno==4; o.cancelMode=storno==1 || storno==3?CancelMode.CHARGEABLE:storno==2 || storno==4?CancelMode.FREE:null; o.status=o.cancelMode!=null?OrderStatus.CANCELLED:switch(o.legacyStatus){case 0->OrderStatus.OFFERED;case 1->OrderStatus.IN_PROGRESS;case 2->OrderStatus.COMPLETED;default->OrderStatus.DISPOSITION;}; o.legacyStatusLabel=switch(o.legacyStatus){case 9->"Disposition";case 8->"Autoranking";case 0->"Offen / angeboten";case 1->"Laufend";case 2->"Erledigt";default->"Legacy-Status "+o.legacyStatus;}; if(o.cancelMode!=null) o.legacyStatusLabel=switch(storno){case 1->"Storniert · kostenpflichtig";case 2->"Storniert · kostenfrei";case 3->"Storno-Gutschrift · kostenpflichtiges Storno";case 4->"Storno-Gutschrift · kostenfreies Storno";default->"Storno";}; o.legacyService=id(row,"jb_service");o.service=text(row,"service_label");o.vehicle=text(row,"vehicle_label"); o.pickupAt=dateTime(row,"jb_ordertime");o.reference=text(row,"jb_commission_no");o.costCenter=text(row,"app_cost_center");o.notes=text(row,"jb_dispoinfo"); o.weight=decimal(row,"jb_weight");o.netPrice=decimal(row,"jb_totalprice");o.courierPrice=decimal(row,"jb_cr_price");o.vatRate=decimal(row,"jb_sales_tax_rate"); o.createdAt=dateTime(row,"jb_createtime");o.updatedAt=dateTime(row,"jb_modify");o.deliveredAt=dateTime(row,"jb_finishtime"); return o; } static Stop stop(Map row) { Stop s=new Stop();s.id=id(row,"tr_id");s.sequence=(int)id(row,"tr_sort"); // Existing VoTiAn tour field is preserved through the explicit warehouse direction mapping. // job_options.php and get_saved_tour.php: bit 2 = collection, bit 1 = delivery. // The old get_tr_ware_from_to_text helper contains the inverse labels. s.type=switch((int)id(row,"tr_ware_from_to")){case 2->StopType.PICKUP;case 1->StopType.DELIVERY;case 3->StopType.BOTH;default->s.sequence==1?StopType.PICKUP:StopType.DELIVERY;}; s.name=join(text(row,"tr_comp"),text(row,"tr_comp2"));s.street=join(text(row,"ad_street"),text(row,"tr_hsno")); s.postalCode=text(row,"ad_zipcode");s.city=text(row,"ad_city");s.contact=text(row,"tr_person");s.phone=text(row,"tr_phone");s.note=text(row,"tr_remark"); s.completedAt=id(row,"tr_status")==1?dateTime(row,"tr_finishtime"):null;s.recipientName=text(row,"tr_signname");return s; } }