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.
94 lines
9.5 KiB
Java
94 lines
9.5 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.util.*;
|
|
import static de.stadtbote.votian.AdminSupport.*;
|
|
import static de.stadtbote.votian.LegacyMapping.*;
|
|
import static de.stadtbote.votian.OrderEntryService.fields;
|
|
|
|
/** Customer mail controls from the current customer_special.php; saving never sends mail. */
|
|
@RestController
|
|
@RequestMapping("/api/master/customers/{customer}/mail-settings")
|
|
class CustomerMailSettings {
|
|
static final List<Integer> BITS=List.of(0,2,3,4,6,7,8,9);
|
|
static final List<String> PARAMETERS=List.of("JOBDETAILS_EMAIL_LANGUAGE_","JOB_MAIL_STATION_ARRIVAL_TIME_","JOBDETAILS_EMAIL_NO_FOOTER_");
|
|
static final String JAM="CS_JB_JAM_WAITTIME_AD_HOC_DEFAULT";
|
|
final DatabaseService db; final BusinessSupport support;
|
|
CustomerMailSettings(DatabaseService db,BusinessSupport support){this.db=db;this.support=support;}
|
|
record Values(int language,boolean arrivalTime,boolean noFooter,Map<Integer,Boolean> extraFields,boolean jamMail,int jamMinutes){}
|
|
record Draft(String revision,Values values){}
|
|
record Command(String requestId,Draft draft,String fingerprint){}
|
|
record View(long customer,String label,String revision,Values values,int jamDefault,List<String> warnings){}
|
|
record Preview(String fingerprint,Values before,Values after,List<String> warnings){}
|
|
record State(Map<String,Object> owner,List<Map<String,Object>> parameters,View view){}
|
|
@GetMapping View get(Authentication a,@PathVariable long customer){return view(ApiController.user(a),customer);}
|
|
@PostMapping("/preview") Preview check(Authentication a,@PathVariable long customer,@RequestBody Draft d){return preview(ApiController.user(a),customer,d);}
|
|
@PostMapping Map<String,Object> post(Authentication a,@PathVariable long customer,@RequestBody Command c){return save(ApiController.user(a),customer,c);}
|
|
LegacyUser access(LegacyUser actor){actor.require(0);actor.require(2);var u=new WorkspaceHeadquarters(db).select(actor,actor.headquartersId());u.require(0);u.require(2);return u;}
|
|
State state(LegacyUser u,long customer){
|
|
if(customer<=0)throw bad("Ungültiger Kunde.");
|
|
var owner=one(db.queryForList("SELECT cs_id,cs_eid,customer.hq_id,customer.cmp_id,cmp_comp,cmp_comp2,cs_jbstatusmail_fields,cs_jb_jam_waittime,cs_jb_jam_waittime_minutes FROM customer JOIN company ON company.cmp_id=customer.cmp_id WHERE cs_id=? AND customer.hq_id=?",customer,u.headquartersId()));
|
|
var rows=db.queryForList("SELECT par_id,par_key,md_id,hq_id,emp_id,par_value,par_text FROM parameter WHERE par_key IN (?,?,?,?) ORDER BY par_key,hq_id,par_id",PARAMETERS.get(0)+customer,PARAMETERS.get(1)+customer,PARAMETERS.get(2)+customer,JAM);
|
|
var customerRows=new HashMap<String,String>();var defaults=new HashMap<Long,String>();
|
|
for(var row:rows){
|
|
String key=text(row,"par_key");long hq=id(row,"hq_id");
|
|
if(key.equals(JAM)){
|
|
if((hq==0||hq==u.headquartersId())&&id(row,"md_id")==0&&id(row,"emp_id")==0&&defaults.put(hq,text(row,"par_value"))!=null)throw bad("Mehrdeutige Wartezeitvorgabe.");
|
|
}else{
|
|
if(id(row,"md_id")!=0||hq!=0||id(row,"emp_id")!=0)throw bad("Abweichender Geltungsbereich der Kunden-Mailvorgaben.");
|
|
if(customerRows.put(key,text(row,"par_value"))!=null)throw bad("Mehrdeutige Kunden-Mailvorgaben.");
|
|
}
|
|
}
|
|
String bits=text(owner,"cs_jbstatusmail_fields");if(!bits.matches("[01]{0,100}"))throw bad("Die gespeicherten Mailfelder sind nicht lesbar. Bitte den Bestand klären.");
|
|
var extra=new LinkedHashMap<Integer,Boolean>();for(int bit:BITS)extra.put(bit,bit<bits.length()&&bits.charAt(bit)=='1');
|
|
int language=binary(customerRows.get(PARAMETERS.get(0)+customer)),arrival=binary(customerRows.get(PARAMETERS.get(1)+customer)),footer=binary(customerRows.get(PARAMETERS.get(2)+customer));
|
|
String defaultValue=defaults.getOrDefault(u.headquartersId(),"");if(defaultValue.isBlank())defaultValue=defaults.getOrDefault(0L,"");
|
|
int wait=defaultValue.matches("[0-9]{1,6}")?Integer.parseInt(defaultValue):0;
|
|
var warnings=new ArrayList<String>();warnings.add("Diese Vorgaben gelten auch für die Altanwendung. Automatische Mails werden weiterhin durch die dort eingerichteten Dienste verarbeitet; das Speichern hier versendet keine Mail.");
|
|
warnings.add("Die ausgewählten Inhaltsfelder werden genau so gespeichert. Beim späteren Speichern im PHP können dessen Niederlassungsstandards Rechnungstext und Kundenmail-Bemerkungen wieder aktivieren.");
|
|
if(wait<=0)warnings.add("Für das Aktivieren der Vermittlungsstau-Mail fehlt eine positive Standardwartezeit, sofern der Kunde noch keine eigene hat.");
|
|
var values=new Values(language,arrival==1,footer==1,Map.copyOf(extra),id(owner,"cs_jb_jam_waittime")>0,(int)id(owner,"cs_jb_jam_waittime_minutes"));
|
|
var view=new View(customer,String.join(" · ",List.of(text(owner,"cs_eid"),text(owner,"cmp_comp"),text(owner,"cmp_comp2")).stream().filter(s->!s.isBlank()).toList()),revision(List.of(owner,rows)),values,wait,List.copyOf(warnings));
|
|
return new State(owner,rows,view);
|
|
}
|
|
static int binary(String value){if(value==null||value.isBlank())return 0;if(!value.equals("0")&&!value.equals("1"))throw bad("Die Kundenvorgabe enthält einen anderen Wert als 0 oder 1. Bitte den Bestand klären.");return Integer.parseInt(value);}
|
|
static void validate(Draft d){
|
|
if(d==null||d.revision()==null||!d.revision().matches("[a-f0-9]{64}")||d.values()==null)throw bad("Bestandsrevision oder Mailvorgaben fehlen.");
|
|
var v=d.values();if(v.language()<0||v.language()>1)throw bad("Bitte Deutsch oder Englisch wählen.");
|
|
if(v.extraFields()==null||!v.extraFields().keySet().equals(new HashSet<>(BITS))||v.extraFields().values().stream().anyMatch(Objects::isNull))throw bad("Die Mailfelder sind unvollständig oder unbekannt.");
|
|
if(v.jamMinutes()<0||v.jamMinutes()>60||v.jamMinutes()%5!=0)throw bad("Bitte eine Wartezeit von 0 bis 60 Minuten in Fünferschritten wählen.");
|
|
}
|
|
Preview prepare(LegacyUser u,long customer,Draft d,State s){
|
|
if(!d.revision().equals(s.view().revision()))throw new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.CONFLICT,"Die Kunden-Mailvorgaben wurden inzwischen geändert. Bitte neu laden.");
|
|
if(d.values().jamMail()&&id(s.owner(),"cs_jb_jam_waittime")<=0&&s.view().jamDefault()<=0)throw bad("Für Vermittlungsstau-Mails fehlt eine positive Standardwartezeit.");
|
|
return new Preview(revision(List.of(u.userId(),u.originalHeadquartersId(),u.headquartersId(),customer,d)),s.view().values(),d.values(),s.view().warnings());
|
|
}
|
|
View view(LegacyUser actor,long customer){return db.readTransaction(()->state(access(actor),customer).view());}
|
|
Preview preview(LegacyUser actor,long customer,Draft d){actor.require(0);actor.require(2);validate(d);return db.readTransaction(()->{var u=access(actor);return prepare(u,customer,d,state(u,customer));});}
|
|
Map<String,Object> save(LegacyUser actor,long customer,Command c){
|
|
actor.require(0);actor.require(2);if(c==null)throw bad("Änderung fehlt.");BusinessSupport.token(c.requestId());validate(c.draft());if(c.fingerprint()==null||!c.fingerprint().matches("[a-f0-9]{64}"))throw bad("Bitte die Änderung zuerst prüfen.");
|
|
access(actor);
|
|
return db.customerMailSettingsWriteTransaction(()->{
|
|
var u=access(actor);var s=state(u,customer);var receipt=List.of("CUSTOMER_MAIL",customer,c.draft(),c.fingerprint());
|
|
var previous=support.receipt(u,c.requestId(),receipt);if(previous!=null)return fields("changed",previous,"repeated",true);
|
|
var p=prepare(u,customer,c.draft(),s);if(!p.fingerprint().equals(c.fingerprint()))throw bad("Die Änderungsvorschau passt nicht zu diesen Angaben.");
|
|
var v=p.after();boolean changed=!p.before().equals(v);
|
|
if(changed){
|
|
String original=text(s.owner(),"cs_jbstatusmail_fields");var bits=new StringBuilder(original+"0".repeat(Math.max(0,10-original.length())));for(int bit:BITS)bits.setCharAt(bit,v.extraFields().get(bit)?'1':'0');
|
|
long wait=v.jamMail()?(id(s.owner(),"cs_jb_jam_waittime")>0?id(s.owner(),"cs_jb_jam_waittime"):s.view().jamDefault()):0;
|
|
db.update("UPDATE customer SET cs_jbstatusmail_fields=?,cs_jb_jam_waittime=?,cs_jb_jam_waittime_minutes=? WHERE cs_id=? AND hq_id=?",bits.toString(),wait,v.jamMinutes(),customer,u.headquartersId());
|
|
var vals=List.of(v.language(),v.arrivalTime()?1:0,v.noFooter()?1:0);
|
|
for(int i=0;i<PARAMETERS.size();i++){
|
|
String key=PARAMETERS.get(i)+customer,value=vals.get(i).toString();var old=s.parameters().stream().filter(r->key.equals(text(r,"par_key"))).findFirst().orElse(null);
|
|
if(old==null){if(!value.equals("0"))db.insert("parameter",fields("par_key",key,"md_id",0,"hq_id",0,"emp_id",0,"par_value",value,"par_text",""));}
|
|
else if(!value.equals(text(old,"par_value")))db.update("UPDATE parameter SET par_value=? WHERE par_id=?",value,id(old,"par_id"));
|
|
}
|
|
support.audit(u,0,customer,0,203,"Kunden-Mailvorgaben geändert · Sprache, Inhaltsfelder und Versandoptionen",c.requestId());
|
|
}
|
|
support.receipt(u,c.requestId(),receipt,changed?1:0);return fields("changed",changed?1:0,"repeated",false);
|
|
});
|
|
}
|
|
}
|