Ergänze Auftragsbearbeitung, Disposition, Stammdaten, Kunden- und Transporteurportale, Lagerverwaltung, Abrechnung, Exporte und Statistik. Füge Kontoeinstellungen, Mitteilungen, Automatikdienste und Betriebsstatus sowie die zugehörigen Tests und PHP-Abgleichsdokumentation hinzu. Vergrößere die persönlichen Auftragslisten auf einheitliche Höhen und richte Statusanzeigen sowie die Ranking-Vorschau-Steuerelemente bündig aus. Validierung: 44 Frontendtests und Produktionsbuild erfolgreich. Backend funktional unverändert gegenüber dem vollständigen Lauf mit 1.624 erfolgreichen und drei übersprungenen Tests. Layout im Browser und lokale Bereitstellung geprüft.
43 lines
6.0 KiB
Java
43 lines
6.0 KiB
Java
package de.stadtbote.votian;
|
|
|
|
import de.stadtbote.votian.database.DatabaseService;
|
|
import org.springframework.security.core.Authentication;
|
|
import org.springframework.web.bind.annotation.*;
|
|
import java.math.BigDecimal;
|
|
import java.util.*;
|
|
import static de.stadtbote.votian.AdminSupport.*;
|
|
import static de.stadtbote.votian.LegacyMapping.*;
|
|
import static de.stadtbote.votian.OrderEntryService.fields;
|
|
|
|
/** Customer detail articles and gross calculator lines from include/inc_job.inc.php. */
|
|
@RestController
|
|
@RequestMapping("/api/customer-portal/orders/{order}/items")
|
|
class CustomerOrderItems {
|
|
static final List<String> KEYS=List.of("at_id","trat_quantity","trat_sort","trat_packingpieces","trat_serialno","trat_state","trat_remark","trat_price","trat_name","trat_remark","trat_weight","trat_id","tr_id","trat_int_serialno");
|
|
static final List<String> LABELS=List.of("ID","Menge","Sortierung","Packstücke","Seriennummer","Status","Bemerkung","Preis","Nummer","Artikelbeschreibung","Gewicht","Positions-ID","Stations-ID","Interne Seriennummer");
|
|
final DatabaseService db;final CustomerPortal portal;
|
|
CustomerOrderItems(DatabaseService db,CustomerPortal portal){this.db=db;this.portal=portal;}
|
|
@GetMapping Object view(Authentication a,@PathVariable long order){return view(ApiController.user(a),order);}
|
|
String parameter(long hq,String key){String value=portal.global(hq,key);return value.isEmpty()?portal.global(0,key):value;}
|
|
Map<String,Object> view(LegacyUser u,long order){return db.readTransaction(()->{
|
|
portal.detail(u,new CustomerPortal.Detail(order));
|
|
if(!portal.enabled(u.headquartersId(),"MASK_JOBDETAILS_JBP_BOOKING_ENABLED"))return fields("enabled",false);
|
|
var job=one(db.queryForList("SELECT jb_sales_tax_rate,jb_payment,csc_id_payer FROM job WHERE jb_id=?",order));
|
|
if(id(job,"jb_payment")==1||id(job,"csc_id_payer")==0)return fields("enabled",true,"message","Artikel und Rechnungsposten eines Kostensplits benötigen eine eindeutige Kostenstellenzuordnung.","columns",List.of(),"articles",List.of(),"items",List.of());
|
|
String raw=parameter(u.headquartersId(),"MASK_JOBDETAILS_ARTICLES");if(raw.isBlank())raw="9,8,1,10,3,7";String titles=parameter(u.headquartersId(),"MASK_JOBDETAILS_ARTICLE_HEADLINE");var names=titles.isBlank()?LABELS:Arrays.asList(titles.split(",",-1));var indexes=new ArrayList<Integer>();
|
|
for(String part:raw.split(",",-1)){if(!part.strip().matches("[0-9]{1,2}"))throw bad("Die Artikelspalten sind ungültig.");int index=Integer.parseInt(part.strip());if(index==99)continue;if(index<0||index>=KEYS.size())throw bad("Eine Artikelspalte benötigt einen aktualisierten Feldabgleich.");indexes.add(index);if(indexes.size()>30)throw bad("Zu viele Artikelspalten.");}
|
|
boolean scans=parameter(u.headquartersId(),"MASK_JOBDETAILS_ARTICLE_SCAN_EVENTS").equals("1");if(scans&&!indexes.contains(4))indexes.add(4);
|
|
var articles=db.queryForList("SELECT trat_id,tr_id,at_id,trat_quantity,trat_price,trat_sort,trat_packingpieces,trat_weight,trat_int_serialno,trat_serialno,trat_state,trat_remark,trat_name FROM tourarticle WHERE jb_id=? AND tr_sort=2 ORDER BY trat_sort,trat_id LIMIT 1001",order);
|
|
if(articles.size()>1000)throw bad("Mehr als 1.000 Artikel im Kundenauftrag.");
|
|
var rows=new ArrayList<Map<String,Object>>();BigDecimal weight=BigDecimal.ZERO,price=BigDecimal.ZERO,pieces=BigDecimal.ZERO;
|
|
for(var article:articles){var cells=new ArrayList<String>();for(int index:indexes){String value=text(article,KEYS.get(index));if(index==9&&value.length()>50)value=value.substring(0,50);cells.add(value);}rows.add(fields("id",id(article,"trat_id"),"cells",cells));weight=weight.add(BillingService.nz(decimal(article,"trat_weight")));price=price.add(BillingService.nz(decimal(article,"trat_price")));pieces=pieces.add(BillingService.nz(decimal(article,"trat_packingpieces")));}
|
|
var columns=indexes.stream().map(i->fields("label",i<names.size()&&!names.get(i).isBlank()?names.get(i):LABELS.get(i),"type",i==7?"money":i==10?"decimal":"text")).toList();
|
|
// PHP treats the stored value as a tax-table ID here and falls back to 19 percent.
|
|
BigDecimal tax=new BigDecimal("19");BigDecimal storedTax=BillingService.nz(decimal(job,"jb_sales_tax_rate"));long taxId=storedTax.stripTrailingZeros().scale()<=0?storedTax.longValue():0;if(taxId>0){var values=db.queryForList("SELECT tx_value FROM tax WHERE tx_id=?",taxId);if(values.size()>1)throw bad("Der Umsatzsteuersatz ist mehrdeutig.");if(!values.isEmpty()&&values.getFirst().get("tx_value")!=null)tax=decimal(values.getFirst(),"tx_value");}if(tax.signum()<0||tax.compareTo(BigDecimal.valueOf(100))>0)throw bad("Ungültiger Umsatzsteuersatz.");BigDecimal factor=BigDecimal.ONE.add(tax.movePointLeft(2)),total=BigDecimal.ZERO;
|
|
var calc=db.queryForList("SELECT srv_name,srvt_name,jbc_amount,jbc_price,jbc_totalprice FROM jobcalculator WHERE jb_id=? AND tr_sort=2 ORDER BY srv_name,srv_id,srvt_id,tr_id LIMIT 1001",order);if(calc.size()>1000)throw bad("Mehr als 1.000 Rechnungsposten im Kundenauftrag.");var items=new ArrayList<Map<String,Object>>();
|
|
for(var item:calc){String name=text(item,"srv_name");if(name.length()>50)name=name.substring(0,50);BigDecimal value=BillingService.nz(decimal(item,"jbc_totalprice"));items.add(fields("name",name,"quantity",decimal(item,"jbc_amount"),"unitPrice",BillingService.money(BillingService.nz(decimal(item,"jbc_price")).multiply(factor)),"price",BillingService.money(value.multiply(factor))));total=total.add(value);}
|
|
var extra=db.queryForList("SELECT gdc_content FROM genericdatacontainer WHERE gdc_obj_type='jb' AND gdc_obj_id=? AND gdc_gen_fieldname='jb_addinfo'",order);if(extra.size()>1)throw bad("Die Artikel-Zusatzinformation ist mehrdeutig.");
|
|
return fields("enabled",true,"message","","columns",columns,"articles",rows,"items",items,"weight",weight,"pieces",pieces,"goodsValue",BillingService.money(price),"total",BillingService.money(total.multiply(factor)),"tax",tax,"additional",extra.isEmpty()?"":OrderDeliveryData.publicRemark(text(extra.getFirst(),"gdc_content")),"scanEvents",scans);
|
|
});}
|
|
}
|