* GPL v3+ */ require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; /** * CRUD-Klasse für einen Bericht. * Eine Bericht-Instanz gehört zu einem Parent-Objekt (invoice|order|propal) * und enthält n Seiten (BerichtPage). */ class Bericht extends CommonObject { public $element = 'bericht'; public $table_element = 'bericht'; public $picto = 'fa-file-pdf'; public $id; public $entity; public $ref; public $titel; public $element_type; // invoice | order | propal public $fk_element; public $auftragsnummer; public $template_odt; public $page_format = 'A4'; // A4, A3, Letter public $page_orientation = 'P'; // P=Portrait, L=Landscape public $status; // 0 = Entwurf, 1 = Final public $final_pdf_path; public $fk_user_creat; public $fk_user_modif; public $datec; public $tms; public $note; const STATUS_DRAFT = 0; const STATUS_FINAL = 1; public function __construct(DoliDB $db) { $this->db = $db; } public function create(User $user, $notrigger = 0) { global $conf; $this->entity = $conf->entity; $this->datec = dol_now(); $this->status = self::STATUS_DRAFT; $this->fk_user_creat = $user->id; if (empty($this->ref)) { $this->ref = 'BR'.date('ymd-His', $this->datec); } $sql = "INSERT INTO ".$this->db->prefix()."bericht (" ."entity, ref, titel, element_type, fk_element, auftragsnummer, template_odt, page_format, page_orientation, status, fk_user_creat, datec, note" .") VALUES (" .((int) $this->entity)."," ."'".$this->db->escape($this->ref)."'," .($this->titel ? "'".$this->db->escape($this->titel)."'" : "NULL")."," ."'".$this->db->escape($this->element_type)."'," .((int) $this->fk_element)."," .($this->auftragsnummer ? "'".$this->db->escape($this->auftragsnummer)."'" : "NULL")."," .($this->template_odt ? "'".$this->db->escape($this->template_odt)."'" : "NULL")."," ."'".$this->db->escape($this->page_format)."'," ."'".$this->db->escape($this->page_orientation)."'," .((int) $this->status)."," .((int) $this->fk_user_creat)."," ."'".$this->db->idate($this->datec)."'," .($this->note ? "'".$this->db->escape($this->note)."'" : "NULL") .")"; $this->db->begin(); $res = $this->db->query($sql); if (!$res) { $this->error = $this->db->lasterror(); $this->db->rollback(); return -1; } $this->id = $this->db->last_insert_id($this->db->prefix()."bericht"); $this->db->commit(); return $this->id; } public function fetch($id) { $sql = "SELECT rowid, entity, ref, titel, element_type, fk_element, auftragsnummer, template_odt," ." page_format, page_orientation," ." status, final_pdf_path, fk_user_creat, fk_user_modif, datec, tms, note" ." FROM ".$this->db->prefix()."bericht WHERE rowid = ".((int) $id); $res = $this->db->query($sql); if (!$res) { $this->error = $this->db->lasterror(); return -1; } if ($this->db->num_rows($res) == 0) { return 0; } $obj = $this->db->fetch_object($res); $this->id = $obj->rowid; $this->entity = $obj->entity; $this->ref = $obj->ref; $this->titel = $obj->titel; $this->element_type = $obj->element_type; $this->fk_element = $obj->fk_element; $this->auftragsnummer = $obj->auftragsnummer; $this->template_odt = $obj->template_odt; $this->page_format = $obj->page_format ?: 'A4'; $this->page_orientation= $obj->page_orientation ?: 'P'; $this->status = (int) $obj->status; $this->final_pdf_path = $obj->final_pdf_path; $this->fk_user_creat = $obj->fk_user_creat; $this->fk_user_modif = $obj->fk_user_modif; $this->datec = $this->db->jdate($obj->datec); $this->tms = $this->db->jdate($obj->tms); $this->note = $obj->note; return 1; } public function update(User $user, $notrigger = 0) { $sql = "UPDATE ".$this->db->prefix()."bericht SET " ."titel = ".($this->titel ? "'".$this->db->escape($this->titel)."'" : "NULL")."," ."auftragsnummer = ".($this->auftragsnummer ? "'".$this->db->escape($this->auftragsnummer)."'" : "NULL")."," ."template_odt = ".($this->template_odt ? "'".$this->db->escape($this->template_odt)."'" : "NULL")."," ."page_format = '".$this->db->escape($this->page_format ?: 'A4')."'," ."page_orientation = '".$this->db->escape($this->page_orientation ?: 'P')."'," ."status = ".((int) $this->status)."," ."final_pdf_path = ".($this->final_pdf_path ? "'".$this->db->escape($this->final_pdf_path)."'" : "NULL")."," ."note = ".($this->note ? "'".$this->db->escape($this->note)."'" : "NULL")."," ."fk_user_modif = ".((int) $user->id) ." WHERE rowid = ".((int) $this->id); $res = $this->db->query($sql); if (!$res) { $this->error = $this->db->lasterror(); return -1; } return 1; } public function delete(User $user, $notrigger = 0) { $this->db->begin(); // Seiten löschen (CASCADE räumt's bereits, aber explizit für robustes Verhalten) $this->db->query("DELETE FROM ".$this->db->prefix()."bericht_page WHERE fk_bericht = ".((int) $this->id)); $res = $this->db->query("DELETE FROM ".$this->db->prefix()."bericht WHERE rowid = ".((int) $this->id)); if (!$res) { $this->error = $this->db->lasterror(); $this->db->rollback(); return -1; } $this->db->commit(); return 1; } /** * Liefert alle Berichte zu einem Parent-Objekt (direkt zugeordnet). * * @param string $element_type invoice | order | propal * @param int $fk_element ID des Parent-Objekts * @return Bericht[] */ public static function fetchAllForElement(DoliDB $db, $element_type, $fk_element) { $list = array(); $sql = "SELECT rowid FROM ".$db->prefix()."bericht" ." WHERE element_type = '".$db->escape($element_type)."'" ." AND fk_element = ".((int) $fk_element) ." ORDER BY datec DESC"; $res = $db->query($sql); if (!$res) return $list; while ($obj = $db->fetch_object($res)) { $b = new self($db); if ($b->fetch($obj->rowid) > 0) { $list[] = $b; } } return $list; } /** * Liefert Berichte, die per llx_element_element zusätzlich zu einem Parent verknüpft wurden. */ public static function fetchLinkedForElement(DoliDB $db, $targettype, $fk_target) { $list = array(); $sql = "SELECT b.rowid FROM ".$db->prefix()."bericht b" ." JOIN ".$db->prefix()."element_element e ON e.fk_source = b.rowid" ." WHERE e.sourcetype = 'bericht'" ." AND e.targettype = '".$db->escape($targettype)."'" ." AND e.fk_target = ".((int) $fk_target) ." ORDER BY b.datec DESC"; $res = $db->query($sql); if (!$res) return $list; while ($obj = $db->fetch_object($res)) { $b = new self($db); if ($b->fetch($obj->rowid) > 0) { $list[] = $b; } } return $list; } /** * Erzeugt eine n:m-Verknüpfung zwischen einem Bericht und einem anderen Element. */ public function linkToElement($targettype, $fk_target) { // Schon vorhanden? $sql = "SELECT rowid FROM ".$this->db->prefix()."element_element" ." WHERE sourcetype = 'bericht' AND fk_source = ".((int) $this->id) ." AND targettype = '".$this->db->escape($targettype)."'" ." AND fk_target = ".((int) $fk_target); $res = $this->db->query($sql); if ($res && $this->db->num_rows($res) > 0) return 1; $ins = "INSERT INTO ".$this->db->prefix()."element_element" ." (fk_source, sourcetype, fk_target, targettype) VALUES (" .((int) $this->id).", 'bericht', ".((int) $fk_target).", '".$this->db->escape($targettype)."')"; return $this->db->query($ins) ? 1 : -1; } /** * Entfernt eine n:m-Verknüpfung. */ public function unlinkFromElement($targettype, $fk_target) { $sql = "DELETE FROM ".$this->db->prefix()."element_element" ." WHERE sourcetype = 'bericht' AND fk_source = ".((int) $this->id) ." AND targettype = '".$this->db->escape($targettype)."'" ." AND fk_target = ".((int) $fk_target); return $this->db->query($sql) ? 1 : -1; } public function getLibStatut($mode = 0) { global $langs; $langs->load("bericht@bericht"); if ($this->status == self::STATUS_FINAL) { return ''.$langs->trans("BerichtStatusFinal").''; } return ''.$langs->trans("BerichtStatusDraft").''; } } /** * Eine Seite eines Berichts. * * Layout-Modi: * single = ein Bild/PDF direkt auf source_path * grid_2 = 2 Bilder nebeneinander (über llx_bericht_page_image) * grid_4 = 2x2 Grid * grid_6 = 3x2 Grid * * Bei single werden image_scale + image_align angewendet: * image_scale: 1.0 / 0.7 / 0.5 / 0.3 * image_align: fit / center / topleft / topright / bottomleft / bottomright */ class BerichtPage { public $db; public $id; public $fk_bericht; public $page_order; public $source_type; public $source_path; public $source_page; public $rotation; public $fabric_json; public $note; public $layout = 'single'; public $image_scale = 1.0; public $image_align = 'fit'; public function __construct(DoliDB $db) { $this->db = $db; } /** * Liefert die Bilder einer Multi-Image-Seite (layout != single). * @return array [['rowid'=>..., 'slot'=>..., 'source_path'=>..., 'rotation'=>...], ...] */ public function getImages() { $list = array(); if ($this->layout === 'single') return $list; $sql = "SELECT rowid, slot, source_path, rotation FROM ".$this->db->prefix()."bericht_page_image" ." WHERE fk_page = ".((int) $this->id) ." ORDER BY slot ASC"; $res = $this->db->query($sql); if (!$res) return $list; while ($obj = $this->db->fetch_object($res)) { $list[] = array( 'rowid' => (int) $obj->rowid, 'slot' => (int) $obj->slot, 'source_path' => $obj->source_path, 'rotation' => (int) $obj->rotation, ); } return $list; } /** * Setzt das Bild eines Slots (überschreibt vorhandenes). */ public function setSlotImage($slot, $source_path, $rotation = 0) { $this->db->query("DELETE FROM ".$this->db->prefix()."bericht_page_image" ." WHERE fk_page = ".((int) $this->id)." AND slot = ".((int) $slot)); $sql = "INSERT INTO ".$this->db->prefix()."bericht_page_image (fk_page, slot, source_path, rotation)" ." VALUES (".((int) $this->id).", ".((int) $slot).", '".$this->db->escape($source_path)."', ".((int) $rotation).")"; return $this->db->query($sql) ? 1 : -1; } public function clearImages() { return $this->db->query("DELETE FROM ".$this->db->prefix()."bericht_page_image WHERE fk_page = ".((int) $this->id)) ? 1 : -1; } /** * Anzahl Slots eines Layouts. */ public static function slotCountForLayout($layout) { return array( 'single' => 1, 'grid_2' => 2, 'grid_2v' => 2, 'grid_4' => 4, 'grid_6' => 6, )[$layout] ?? 1; } /** * Berechnet die Slot-Rectangles innerhalb einer Seite (mm) für ein Grid-Layout. * @return array [['x'=>..,'y'=>..,'w'=>..,'h'=>..], ...] */ public static function slotRects($layout, $pageW, $pageH, $margin = 10, $gap = 4) { $rects = array(); $iw = $pageW - 2 * $margin; $ih = $pageH - 2 * $margin - 10; // 10mm Notiz unten if ($layout === 'grid_2') { // 2 Bilder horizontal: 1 oben, 1 unten $h = ($ih - $gap) / 2; for ($i = 0; $i < 2; $i++) { $rects[] = array('x' => $margin, 'y' => $margin + $i * ($h + $gap), 'w' => $iw, 'h' => $h); } } elseif ($layout === 'grid_2v') { // 2 Bilder vertikal nebeneinander $w = ($iw - $gap) / 2; for ($i = 0; $i < 2; $i++) { $rects[] = array('x' => $margin + $i * ($w + $gap), 'y' => $margin, 'w' => $w, 'h' => $ih); } } elseif ($layout === 'grid_4') { $w = ($iw - $gap) / 2; $h = ($ih - $gap) / 2; for ($r = 0; $r < 2; $r++) { for ($c = 0; $c < 2; $c++) { $rects[] = array( 'x' => $margin + $c * ($w + $gap), 'y' => $margin + $r * ($h + $gap), 'w' => $w, 'h' => $h ); } } } elseif ($layout === 'grid_6') { $w = ($iw - 2 * $gap) / 3; $h = ($ih - $gap) / 2; for ($r = 0; $r < 2; $r++) { for ($c = 0; $c < 3; $c++) { $rects[] = array( 'x' => $margin + $c * ($w + $gap), 'y' => $margin + $r * ($h + $gap), 'w' => $w, 'h' => $h ); } } } return $rects; } public function create() { $sql = "INSERT INTO ".$this->db->prefix()."bericht_page (" ."fk_bericht, page_order, source_type, source_path, source_page, rotation, fabric_json, note, layout, image_scale, image_align" .") VALUES (" .((int) $this->fk_bericht)."," .((int) $this->page_order)."," ."'".$this->db->escape($this->source_type)."'," ."'".$this->db->escape($this->source_path)."'," .($this->source_page !== null ? (int) $this->source_page : "NULL")."," .((int) ($this->rotation ?? 0))."," .($this->fabric_json !== null ? "'".$this->db->escape($this->fabric_json)."'" : "NULL")."," .($this->note ? "'".$this->db->escape($this->note)."'" : "NULL")."," ."'".$this->db->escape($this->layout ?: 'single')."'," .((float) ($this->image_scale ?: 1.0))."," ."'".$this->db->escape($this->image_align ?: 'fit')."'" .")"; $res = $this->db->query($sql); if (!$res) return -1; $this->id = $this->db->last_insert_id($this->db->prefix()."bericht_page"); return $this->id; } public function update() { $sql = "UPDATE ".$this->db->prefix()."bericht_page SET " ."page_order = ".((int) $this->page_order)."," ."rotation = ".((int) ($this->rotation ?? 0))."," ."fabric_json = ".($this->fabric_json !== null ? "'".$this->db->escape($this->fabric_json)."'" : "NULL")."," ."note = ".($this->note ? "'".$this->db->escape($this->note)."'" : "NULL")."," ."layout = '".$this->db->escape($this->layout ?: 'single')."'," ."image_scale = ".((float) ($this->image_scale ?: 1.0))."," ."image_align = '".$this->db->escape($this->image_align ?: 'fit')."'" ." WHERE rowid = ".((int) $this->id); return $this->db->query($sql) ? 1 : -1; } public function delete() { return $this->db->query("DELETE FROM ".$this->db->prefix()."bericht_page WHERE rowid = ".((int) $this->id)) ? 1 : -1; } public static function fetchAllForBericht(DoliDB $db, $fk_bericht) { $list = array(); $sql = "SELECT rowid, fk_bericht, page_order, source_type, source_path, source_page, rotation, fabric_json, note," ." COALESCE(layout, 'single') AS layout," ." COALESCE(image_scale, 1.0) AS image_scale," ." COALESCE(image_align, 'fit') AS image_align" ." FROM ".$db->prefix()."bericht_page" ." WHERE fk_bericht = ".((int) $fk_bericht) ." ORDER BY page_order ASC, rowid ASC"; $res = $db->query($sql); if (!$res) return $list; while ($obj = $db->fetch_object($res)) { $p = new self($db); $p->id = $obj->rowid; $p->fk_bericht = $obj->fk_bericht; $p->page_order = (int) $obj->page_order; $p->source_type = $obj->source_type; $p->source_path = $obj->source_path; $p->source_page = $obj->source_page !== null ? (int) $obj->source_page : null; $p->rotation = (int) $obj->rotation; $p->fabric_json = $obj->fabric_json; $p->note = $obj->note; $p->layout = $obj->layout; $p->image_scale = (float) $obj->image_scale; $p->image_align = $obj->image_align; $list[] = $p; } return $list; } }