# Paisy — Development Guidelines ## Code Quality Standards ### Logging - Use `@Log4j2` (Lombok) for existing modules that already use Log4j2 (e.g., `AbstractMeldung`) - Use `@Slf4j` (Lombok) for new code — this is the preferred default - Use parameterized logging: `log.debug("Value: {}", value)` — never string concatenation - Some legacy code mixes styles: `log.debug("text " + var)` — refactor to parameterized when touching these lines - Log levels: `debug` for flow tracing, `warn` for recoverable issues, `error` for failures ### Naming Conventions - Package: `com.adp.de.paisy.modules.` for modules, `com.adp.de.` for legacy - German domain terms preserved in class/field names: `Fehlzeiten`, `Lohnkonto`, `Vorlaufsatz`, `Nachlaufsatz`, `Meldekorrekturen` - JAXB-generated classes use German XML element names as Java identifiers: `getAngabenZurPersonAV()`, `getBeschreibungTaetigkeitDE()` - Constants use UPPER_SNAKE_CASE: `EMPTY_DATE_LONG`, `EMPTY_BBNR`, `MAX_DBFZ` - Date formatters as `final` fields: `bausteinf`, `pai022f`, `PAI_SHORT` ### Field Visibility - `protected` for fields shared with subclasses (common in abstract controllers) - `private` with Lombok `@Getter`/`@Setter` for DTOs - `static private final` for constants (PDDI layer uses tab-indented style) ## Structural Conventions ### Module Entry Point Pattern ```java @Slf4j @Service("module-name") @Lazy public class ModuleRunner implements ConsoleService { @Override public void run(String... args) throws Exception { /* ... */ } } ``` ### Abstract Controller Pattern (5/5 files) Business modules use abstract base classes for shared logic: ```java @Log4j2 public abstract class AbstractMeldung { // Shared date formatters, parsers, PAISY interaction protected Person person; protected Fehlzeiten fz; public Person initBaustein(Datenbaustein main, DBNA dbna, ...) { /* ... */ } protected Lohnkonto parseLohnkonto(...) { /* ... */ } } ``` ### Datenbaustein (Data Block) Pattern (5/5 files) Core data exchange pattern — field-based data blocks with positional access: ```java Datenbaustein main = ...; main.setValue("FEKZ", "0"); main.setValue("VSNR", person.getValue("VSNR")); String value = d508b.getValue("F39-AVUWFWZ-10-50-8B"); ``` ### ServiceCenter Singleton Pattern Central access point for PAISY system interaction: ```java ServiceCenter.INSTANCE().getBaustein(Datengruppe.NAME, vorgangsID); ServiceCenter.INSTANCE().getPaisy().pgmFunktionCall("S;" + bbnrvu + ";"); ServiceCenter.INSTANCE().getPaisy().pgmReadLine(); ``` ### EMFactory Singleton Pattern Each module has its own EntityManager factory: ```java EMFactoryEAU emFactory = EMFactoryEAU.getInstance(); FlywayController flyway = new FlywayController(emFactory); flyway.migrate("EAU", "2024_09_08_17_48_11"); ``` ## JAXB XML Binding Pattern (3/5 files) JAXB-generated classes follow a strict pattern for GKV data exchange: ```java @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "steuerungsdaten", "angabenZurPersonAV", ... }) @XmlRootElement(name = "A1_Ausnahmevereinbarung", namespace = "http://www.gkv-datenaustausch.de/XMLSchema/A1_Ausnahme/2.0") public class A1Ausnahmevereinbarung { @XmlElement(name = "Steuerungsdaten", namespace = "http://www.gkv-datenaustausch.de/XMLSchema/A1_Ausnahme/2.0", required = true) protected SteuerungsdatenAGV2Ctp steuerungsdaten; } ``` Key rules: - Nested `public static class` for complex type hierarchies - German Javadoc: `"Ruft den Wert der X-Eigenschaft ab"` / `"Legt den Wert der X-Eigenschaft fest"` - `jakarta.xml.bind.annotation.*` (Jakarta EE, not javax) - `XMLGregorianCalendar` for date fields, `BigInteger` for numeric codes - `KennzeichenAlphanumerischTyp` enum for J/N flag fields ## PDF Generation Pattern (2/5 files) Using OpenPDF (iText fork) for government-compliant PDF reports: ```java Document document = new Document(PageSize.A4, 50, 50, 50, 50); PdfWriter writer = PdfWriter.getInstance(document, baos); document.open(); // Header Paragraph headline = new Paragraph("EuBP", getHeaderFont()); document.add(headline); // Two-column table: Bezeichnung | Inhalt PdfPTable table = new PdfPTable(2); table.setWidthPercentage(75); table.setHorizontalAlignment(Element.ALIGN_LEFT); addTableHeader(table, new String[]{"Bezeichnung", "Inhalt"}); table.addCell(createCell("Verfahrensmerkmal")); table.addCell(createCell(vorlaufsatz.getVerfahrensmerkmal().value)); document.add(table); ``` Font conventions: - Header: Helvetica Bold 18pt - Subheader: Helvetica Bold 14pt - Bold: Helvetica Bold 12pt - Normal: Helvetica 10pt - Table header background: `new Color(220, 220, 220)` ## ISAM/Oracle Trigger Generation (1/5 files) JavaScript code generator for Oracle INSTEAD OF triggers: ```javascript // gen_trigger.js — generates Oracle triggers for COBOL ISAM → Oracle mirroring gen("YP_ABR_STEUER", ` ,MUT_YP_ABR_STEUER.ABSCHNITT ,MUT_YP_ABR_STEUER.BEMERKUNG ... `) ``` Pattern: `IPW_` view → `MUT_` mutation table, with `ak_nr`/`pers_nr` lookup from `PS_YP_PERS_MAIN`. ## Binary Protocol Pattern (1/5 files) Custom binary serialization for PDDI middleware (DocumentStream): - Type-tagged byte protocol: `b_null='~'`, `b_string='S'`, `b_int='i'`, `b_date='d'` - Compact encoding: shorts for small ints, full 8 bytes only when needed - Custom charset: `PaisyCharset.CHARSET` (likely ISO-8859-1) - Inner classes: `BytesOutput`, `Output` (stream-based), `Input` (stream-based) ## Date Handling Patterns ```java // German user-facing format DateTimeFormatter.ofPattern("dd.MM.yyyy") // Internal PAISY format (8-digit) DateTimeFormatter.ofPattern("yyyyMMdd") // Short PAISY format (6-digit, 2-digit year) DateTimeFormatter.ofPattern("yyMMdd") // Null/empty date handling if (value.equals("00.00.0000")) return LocalDate.MAX; if (paidate.equals("0000000")) return LocalDate.MIN; if (paidate.equals("9999999")) return LocalDate.MAX; ``` ## Error Handling - Custom exceptions: `PaisyIOException`, `PaisyRuntimeException`, `AccessDeniedException`, `PaisyNotFoundException`, `UnauthorizedException` - PAISY error responses start with `"F;"` — check before parsing - Null-safe patterns: check for null before processing sections (PDF, data blocks) - Reflection-based construction with explicit error messages for field count mismatches ## Frequently Used Annotations | Annotation | Usage | Frequency | |-----------|-------|-----------| | `@Log4j2` / `@Slf4j` | Logging | Every class | | `@Getter` / `@Setter` | Lombok accessors | DTOs, models | | `@XmlElement` / `@XmlAccessorType` | JAXB binding | XML model classes | | `@XmlRootElement` / `@XmlType` | JAXB root types | Top-level XML types | | `@XmlSchemaType` | JAXB schema mapping | XML fields | | `@Service("name")` / `@Lazy` | Spring service registration | Module entry points | | `@Transactional` | DB transactions | Service methods | | `@Data` / `@Builder` | Lombok DTOs | Model classes | ## Code Idioms - `CommonRoutines.paidate2Datenbaustein()` — convert PAISY date to Datenbaustein format - `CommonRoutines.uuid()` — generate unique dataset IDs - `CommonRoutines.getDocumentPath()` — get output file path - `ServiceCenter.INSTANCE().getPaisy().nextPaisy(line)` — iterate PAISY response lines - `line.split(";")` — semicolon-delimited PAISY response parsing - Streams with `Collectors.toCollection(ArrayList::new)` for mutable result lists