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 BITS=List.of(0,2,3,4,6,7,8,9); static final List 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 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 warnings){} record Preview(String fingerprint,Values before,Values after,List warnings){} record State(Map owner,List> 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 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();var defaults=new HashMap(); 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();for(int bit:BITS)extra.put(bit,bit();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 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;ikey.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); }); } }