mirror of
https://github.com/itflow-org/itflow
synced 2026-09-19 21:25:13 +00:00
Email Invoice Only shows Billing and Primary, Quote shows Important Billing, Primary and Technical contacts that can be selected
This commit is contained in:
@@ -106,15 +106,15 @@ if (isset($_GET['invoice_id'])) {
|
|||||||
}
|
}
|
||||||
$company_logo = escapeHtml($row['company_logo']);
|
$company_logo = escapeHtml($row['company_logo']);
|
||||||
|
|
||||||
// Send Email used to be gated on the PRIMARY contact having an email, which
|
// Must use the same rule as the Send Email picker in
|
||||||
// hid the button on a client whose only emailable contact was a billing or
|
// modals/invoice/invoice_email.php, or the button offers a modal that then
|
||||||
// secondary one. The modal can send to any of them, so gate on whether the
|
// reports there is nobody to send to.
|
||||||
// client has anybody reachable at all.
|
|
||||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(contact_id) AS emailable_contacts FROM contacts
|
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(contact_id) AS emailable_contacts FROM contacts
|
||||||
WHERE contact_client_id = $client_id
|
WHERE contact_client_id = $client_id
|
||||||
AND contact_archived_at IS NULL
|
AND contact_archived_at IS NULL
|
||||||
AND contact_email IS NOT NULL
|
AND contact_email IS NOT NULL
|
||||||
AND contact_email != ''"));
|
AND contact_email != ''
|
||||||
|
" . documentContactFilterSql('invoice')));
|
||||||
$emailable_contacts = intval($row['emailable_contacts']);
|
$emailable_contacts = intval($row['emailable_contacts']);
|
||||||
|
|
||||||
$sql_history = mysqli_query($mysqli, "SELECT history_created_at, history_description, history_status FROM history WHERE history_invoice_id = $invoice_id ORDER BY history_id DESC");
|
$sql_history = mysqli_query($mysqli, "SELECT history_created_at, history_description, history_status FROM history WHERE history_invoice_id = $invoice_id ORDER BY history_id DESC");
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ $client_id = intval($row['invoice_client_id']);
|
|||||||
|
|
||||||
enforceClientAccess();
|
enforceClientAccess();
|
||||||
|
|
||||||
// Everyone at the client who can actually receive mail. Primary and billing
|
// Billing-facing contacts only - see documentContactFilterSql(). Everyone
|
||||||
// contacts lead because they are the ones checked by default below - that
|
// shown is also checked by default, which is the behaviour the old one-click
|
||||||
// pairing is the behaviour the old one-click Send Email link had baked in.
|
// Send Email link had baked in; the checkboxes are here to drop one, not to
|
||||||
|
// go hunting for somebody.
|
||||||
$sql_contacts = mysqli_query(
|
$sql_contacts = mysqli_query(
|
||||||
$mysqli,
|
$mysqli,
|
||||||
"SELECT contact_billing, contact_email, contact_id, contact_name, contact_primary, contact_title
|
"SELECT contact_billing, contact_email, contact_id, contact_name, contact_primary, contact_title
|
||||||
@@ -27,6 +28,7 @@ $sql_contacts = mysqli_query(
|
|||||||
AND contact_archived_at IS NULL
|
AND contact_archived_at IS NULL
|
||||||
AND contact_email IS NOT NULL
|
AND contact_email IS NOT NULL
|
||||||
AND contact_email != ''
|
AND contact_email != ''
|
||||||
|
" . documentContactFilterSql('invoice') . "
|
||||||
ORDER BY contact_primary DESC, contact_billing DESC, contact_name ASC"
|
ORDER BY contact_primary DESC, contact_billing DESC, contact_name ASC"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -51,9 +53,9 @@ ob_start();
|
|||||||
<?php if ($contact_count == 0) { ?>
|
<?php if ($contact_count == 0) { ?>
|
||||||
|
|
||||||
<p class="text-muted mb-0">
|
<p class="text-muted mb-0">
|
||||||
This client has no contacts with an email address, so there is nobody to send to.
|
This client has no primary or billing contact with an email address, so there is
|
||||||
Add a contact with an email address, or use Mark Sent to record that the invoice
|
nobody to send an invoice to. Flag a contact as billing, or use Mark Sent to record
|
||||||
went out some other way.
|
that the invoice went out some other way.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<?php } else { ?>
|
<?php } else { ?>
|
||||||
|
|||||||
@@ -16,15 +16,19 @@ $client_id = intval($row['quote_client_id']);
|
|||||||
|
|
||||||
enforceClientAccess();
|
enforceClientAccess();
|
||||||
|
|
||||||
|
// Wider than the invoice picker - see documentContactFilterSql(). A quote is
|
||||||
|
// read by whoever scoped the work as often as by whoever signs it.
|
||||||
$sql_contacts = mysqli_query(
|
$sql_contacts = mysqli_query(
|
||||||
$mysqli,
|
$mysqli,
|
||||||
"SELECT contact_billing, contact_email, contact_id, contact_name, contact_primary, contact_title
|
"SELECT contact_billing, contact_email, contact_id, contact_important, contact_name,
|
||||||
|
contact_primary, contact_technical, contact_title
|
||||||
FROM contacts
|
FROM contacts
|
||||||
WHERE contact_client_id = $client_id
|
WHERE contact_client_id = $client_id
|
||||||
AND contact_archived_at IS NULL
|
AND contact_archived_at IS NULL
|
||||||
AND contact_email IS NOT NULL
|
AND contact_email IS NOT NULL
|
||||||
AND contact_email != ''
|
AND contact_email != ''
|
||||||
ORDER BY contact_primary DESC, contact_billing DESC, contact_name ASC"
|
" . documentContactFilterSql('quote') . "
|
||||||
|
ORDER BY contact_primary DESC, contact_billing DESC, contact_technical DESC, contact_name ASC"
|
||||||
);
|
);
|
||||||
|
|
||||||
$contact_count = mysqli_num_rows($sql_contacts);
|
$contact_count = mysqli_num_rows($sql_contacts);
|
||||||
@@ -48,9 +52,9 @@ ob_start();
|
|||||||
<?php if ($contact_count == 0) { ?>
|
<?php if ($contact_count == 0) { ?>
|
||||||
|
|
||||||
<p class="text-muted mb-0">
|
<p class="text-muted mb-0">
|
||||||
This client has no contacts with an email address, so there is nobody to send to.
|
This client has no primary, billing, technical or important contact with an email
|
||||||
Add a contact with an email address, or use Mark Sent to record that the quote
|
address, so there is nobody to send a quote to. Flag a contact with one of those
|
||||||
went out some other way.
|
roles, or use Mark Sent to record that the quote went out some other way.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<?php } else { ?>
|
<?php } else { ?>
|
||||||
@@ -68,6 +72,8 @@ ob_start();
|
|||||||
$contact_title = escapeHtml($row['contact_title']);
|
$contact_title = escapeHtml($row['contact_title']);
|
||||||
$contact_primary = intval($row['contact_primary']);
|
$contact_primary = intval($row['contact_primary']);
|
||||||
$contact_billing = intval($row['contact_billing']);
|
$contact_billing = intval($row['contact_billing']);
|
||||||
|
$contact_technical = intval($row['contact_technical']);
|
||||||
|
$contact_important = intval($row['contact_important']);
|
||||||
|
|
||||||
// Default selection reproduces the old link: quotes went to
|
// Default selection reproduces the old link: quotes went to
|
||||||
// the primary contact only. Billing contacts are listed and
|
// the primary contact only. Billing contacts are listed and
|
||||||
@@ -88,6 +94,12 @@ ob_start();
|
|||||||
<?php if ($contact_billing == 1) { ?>
|
<?php if ($contact_billing == 1) { ?>
|
||||||
<span class="badge text-bg-success ms-1">Billing</span>
|
<span class="badge text-bg-success ms-1">Billing</span>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
<?php if ($contact_technical == 1) { ?>
|
||||||
|
<span class="badge text-bg-info ms-1">Technical</span>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($contact_important == 1) { ?>
|
||||||
|
<span class="badge text-bg-warning ms-1">Important</span>
|
||||||
|
<?php } ?>
|
||||||
<?php if (!empty($contact_title)) { ?>
|
<?php if (!empty($contact_title)) { ?>
|
||||||
<span class="text-muted ms-1"><?= $contact_title ?></span>
|
<span class="text-muted ms-1"><?= $contact_title ?></span>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|||||||
@@ -99,15 +99,15 @@ if (isset($_GET['quote_id'])) {
|
|||||||
$company_website = escapeHtml($row['company_website']);
|
$company_website = escapeHtml($row['company_website']);
|
||||||
$company_logo = escapeHtml($row['company_logo']);
|
$company_logo = escapeHtml($row['company_logo']);
|
||||||
|
|
||||||
// Send Email used to be gated on the PRIMARY contact having an email, which
|
// Must use the same rule as the Send Email picker in
|
||||||
// hid the button on a client whose only emailable contact was a billing or
|
// modals/quote/quote_email.php, or the button offers a modal that then
|
||||||
// secondary one. The modal can send to any of them, so gate on whether the
|
// reports there is nobody to send to.
|
||||||
// client has anybody reachable at all.
|
|
||||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(contact_id) AS emailable_contacts FROM contacts
|
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(contact_id) AS emailable_contacts FROM contacts
|
||||||
WHERE contact_client_id = $client_id
|
WHERE contact_client_id = $client_id
|
||||||
AND contact_archived_at IS NULL
|
AND contact_archived_at IS NULL
|
||||||
AND contact_email IS NOT NULL
|
AND contact_email IS NOT NULL
|
||||||
AND contact_email != ''"));
|
AND contact_email != ''
|
||||||
|
" . documentContactFilterSql('quote')));
|
||||||
$emailable_contacts = intval($row['emailable_contacts']);
|
$emailable_contacts = intval($row['emailable_contacts']);
|
||||||
|
|
||||||
$sql_history = mysqli_query($mysqli, "SELECT history_created_at, history_description, history_status FROM history WHERE history_quote_id = $quote_id ORDER BY history_id DESC");
|
$sql_history = mysqli_query($mysqli, "SELECT history_created_at, history_description, history_status FROM history WHERE history_quote_id = $quote_id ORDER BY history_id DESC");
|
||||||
|
|||||||
@@ -786,6 +786,34 @@ function createiCalStrCancel($datetime, $title, $uid) {
|
|||||||
return $cal_event->export();
|
return $cal_event->export();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Which contacts a document's Send Email picker offers, as a SQL fragment.
|
||||||
|
*
|
||||||
|
* A client with fifty contacts should not hand the agent a fifty-row list to
|
||||||
|
* scroll, so the picker is narrowed to the people a given document type is
|
||||||
|
* actually addressed to:
|
||||||
|
*
|
||||||
|
* invoice - primary and billing. A bill goes to whoever pays it.
|
||||||
|
* quote - primary, billing, technical and important. A quote gets read by
|
||||||
|
* the person who scoped the work as often as the one who signs it.
|
||||||
|
*
|
||||||
|
* Lives here rather than inline in the modals because the Send Email button on
|
||||||
|
* agent/invoice.php and agent/quote.php gates on a COUNT using the same rule -
|
||||||
|
* if the two drift, the button appears and then the modal it opens reports
|
||||||
|
* there is nobody to send to.
|
||||||
|
*
|
||||||
|
* Note the post handlers deliberately do NOT re-apply this filter. It is a
|
||||||
|
* shortlist, not a permission boundary: any agent who can send a document can
|
||||||
|
* already set these flags on a contact.
|
||||||
|
*/
|
||||||
|
function documentContactFilterSql($document_type) {
|
||||||
|
if ($document_type === 'quote') {
|
||||||
|
return "AND (contact_primary = 1 OR contact_billing = 1 OR contact_technical = 1 OR contact_important = 1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "AND (contact_primary = 1 OR contact_billing = 1)";
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The delivery methods offered by the Mark Sent modal on invoices and quotes.
|
* The delivery methods offered by the Mark Sent modal on invoices and quotes.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user