Erweitere VoTiAn NG um Fachfunktionen und verbessere die Listenoberfläche
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.
This commit is contained in:
1 parent
363bc27c26
commit
781a2b82dc
1165 files changed
+74986
-1028
No files matched your search
@@ -0,0 +1,93 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user