feat(opportunities): Link zur Ausschreibung als eigenes Feld
Die Uebernahme aus der Akquise schrieb den Link bisher nur in die Notizen. Jetzt hat die Opportunity eine Spalte `link`, gefuellt bei Uebernahme und Kopie; im Dialog steht sie rechts neben der Phase mit Knopf "Oeffnen". schema.sql traegt den Link fuer aeltere Uebernahmen nach. Die Pruefung liegt in server/src/link.js und gilt fuer Akquise und Opportunities gleich. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3e76a73f2e
commit
8cb5cbc7cc
@@ -1,6 +1,7 @@
|
||||
import express from 'express';
|
||||
import { pool } from '../db.js';
|
||||
import { requireAuth } from '../middleware/auth.js';
|
||||
import { normalizeLink } from '../link.js';
|
||||
|
||||
const router = express.Router();
|
||||
router.use(requireAuth);
|
||||
@@ -328,6 +329,10 @@ router.post('/', async (req, res, next) => {
|
||||
.json({ error: `Der Berater ist zu lang (max. ${CONSULTANT_MAX_LENGTH} Zeichen).` });
|
||||
}
|
||||
|
||||
// Link zur Ausschreibung - dieselbe Pruefung wie in der Akquise.
|
||||
const link = normalizeLink(req.body?.link);
|
||||
if (link.error) return res.status(400).json({ error: link.error });
|
||||
|
||||
const contacts = await normalizeContacts(req.body?.contacts ?? []);
|
||||
if (contacts.error) return res.status(400).json({ error: contacts.error });
|
||||
|
||||
@@ -338,14 +343,15 @@ router.post('/', async (req, res, next) => {
|
||||
// Wer direkt in 'Beauftragt' anlegt, hat gerade gewonnen - dann steht das
|
||||
// Datum sofort.
|
||||
`INSERT INTO crm_opportunities
|
||||
(title, company, consultant, description, stage, won_at, probability, daily_rate,
|
||||
(title, company, consultant, link, description, stage, won_at, probability, daily_rate,
|
||||
duration_days, fixed_price, start_date, end_date, last_interaction_at,
|
||||
last_interaction_note, next_step, tags, notes, owner_id, created_by)
|
||||
VALUES (?, ?, ?, ?, ?, ${stage === 'Beauftragt' ? 'UTC_TIMESTAMP()' : 'NULL'}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
VALUES (?, ?, ?, ?, ?, ?, ${stage === 'Beauftragt' ? 'UTC_TIMESTAMP()' : 'NULL'}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
title,
|
||||
emptyToNull(req.body?.company),
|
||||
consultant,
|
||||
link.value,
|
||||
emptyToNull(req.body?.description),
|
||||
stage,
|
||||
Math.round(probability.value ?? 10),
|
||||
@@ -416,6 +422,13 @@ router.patch('/:id', async (req, res, next) => {
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
if (req.body?.link !== undefined) {
|
||||
const link = normalizeLink(req.body.link);
|
||||
if (link.error) return res.status(400).json({ error: link.error });
|
||||
fields.push('link = ?');
|
||||
values.push(link.value);
|
||||
}
|
||||
|
||||
if (req.body?.stage !== undefined) {
|
||||
if (!STAGES.has(req.body.stage)) return res.status(400).json({ error: 'Ungültige Phase.' });
|
||||
fields.push('stage = ?');
|
||||
@@ -708,7 +721,7 @@ router.post('/:id/acquisition', async (req, res, next) => {
|
||||
* fuer einen Kollegen, entsteht je Berater eine Kopie mit eigener Phase - wie
|
||||
* bei der Uebernahme aus der Akquise, nur nachtraeglich.
|
||||
*
|
||||
* Kopiert wird, was das Projekt beschreibt: Titel, Firma, Beschreibung, Zahlen,
|
||||
* Kopiert wird, was das Projekt beschreibt: Titel, Firma, Link, Beschreibung, Zahlen,
|
||||
* Zeitraum, Tags, Notizen, Ansprechpartner, Zustaendiger und die Herkunft aus
|
||||
* der Akquise (damit "Zurueck in die Akquise" auch die Kopien findet). Nicht
|
||||
* kopiert wird, was zum Verlauf des Originals gehoert: letzte Interaktion,
|
||||
@@ -764,15 +777,16 @@ router.post('/:id/copy', async (req, res, next) => {
|
||||
for (const consultant of consultants) {
|
||||
const [created] = await conn.query(
|
||||
`INSERT INTO crm_opportunities
|
||||
(title, company, consultant, acquisition_id, description, stage, won_at, probability,
|
||||
daily_rate, duration_days, fixed_price, start_date, end_date, tags, notes,
|
||||
owner_id, created_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ${stage === 'Beauftragt' ? 'UTC_TIMESTAMP()' : 'NULL'}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
(title, company, consultant, acquisition_id, link, description, stage, won_at,
|
||||
probability, daily_rate, duration_days, fixed_price, start_date, end_date, tags,
|
||||
notes, owner_id, created_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ${stage === 'Beauftragt' ? 'UTC_TIMESTAMP()' : 'NULL'}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
source.title,
|
||||
source.company,
|
||||
consultant,
|
||||
source.acquisition_id,
|
||||
source.link,
|
||||
source.description,
|
||||
stage,
|
||||
source.probability,
|
||||
|
||||
Reference in New Issue
Block a user