Skip to content
This repository has been archived by the owner on Dec 27, 2023. It is now read-only.

Commit

Permalink
feature(Sales/Offer): add offer status field
Browse files Browse the repository at this point in the history
  • Loading branch information
pschuele committed Dec 14, 2021
1 parent 3347368 commit a6b54b5
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 44 deletions.
1 change: 1 addition & 0 deletions tests/tine20/Sales/Document/JsonTest.php
Expand Up @@ -83,6 +83,7 @@ public function testOfferDocumentWithoutRecipient()
$document = new Sales_Model_Document_Offer([
Sales_Model_Document_Offer::FLD_CUSTOMER_ID => $customerData,
Sales_Model_Document_Offer::FLD_RECIPIENT_ID => '',
Sales_Model_Document_Offer::FLD_OFFER_STATUS => Sales_Model_Document_Offer::STATUS_IN_PROCESS,
]);
$document = $this->_instance->saveDocument_Offer($document->toArray(true));

Expand Down
31 changes: 31 additions & 0 deletions tine20/Sales/Config.php
Expand Up @@ -113,6 +113,13 @@ class Sales_Config extends Tinebase_Config_Abstract

const VARIABLE_POSITION_FLAG = 'subProductPositionFlag';

/**
* offer status
*
* @var string
*/
const DOCUMENT_OFFER_STATUS = 'documentOfferStatus';

/**
* Invoice Type
*
Expand Down Expand Up @@ -177,6 +184,30 @@ class Sales_Config extends Tinebase_Config_Abstract
'setByAdminModule' => TRUE,
'default' => 12
),
self::DOCUMENT_OFFER_STATUS => [
//_('Offer Status')
self::LABEL => 'Offer Status',
//_('Possible Offer Status')
self::DESCRIPTION => 'Possible Offer Status',
self::TYPE => self::TYPE_KEYFIELD_CONFIG,
self::CLIENTREGISTRYINCLUDE => true,
self::SETBYADMINMODULE => false,
self::SETBYSETUPMODULE => false,
self::DEFAULT_STR => [
// OFFER_STATUS // keyfield: In Bearbeitung(ungebucht, offen), Zugestellt(gebucht, offen), Beauftragt(gebucht, offen), Abgelehnt(gebucht, geschlossen)
self::RECORDS => [
['id' => Sales_Model_Document_Offer::STATUS_IN_PROCESS, 'value' => 'In process (unbooked, open)',
'icon' => null, 'system' => true], //_('In process (unbooked, open)')
['id' => Sales_Model_Document_Offer::STATUS_DELIVERED, 'value' => 'Delivered (booked, open)',
'icon' => null, 'system' => true], //_('Delivered (booked, open)')
['id' => Sales_Model_Document_Offer::STATUS_ORDERED, 'value' => 'Ordered (booked, open)',
'icon' => null, 'system' => true], //_('Ordered (booked, open)')
['id' => Sales_Model_Document_Offer::STATUS_REJECTED, 'value' => 'Rejected (booked, closed)',
'icon' => null, 'system' => true], //_('Rejected (booked, closed)')
],
self::DEFAULT_STR => Sales_Model_Document_Offer::STATUS_IN_PROCESS,
],
],
self::IGNORE_BILLABLES_BEFORE => array(
//_('Ignore Billables Before Date')
'label' => 'Ignore Billables Before Date',
Expand Down
57 changes: 25 additions & 32 deletions tine20/Sales/Model/Document/Abstract.php
Expand Up @@ -19,57 +19,50 @@ abstract class Sales_Model_Document_Abstract extends Tinebase_Record_NewAbstract
{
//const MODEL_NAME_PART = ''; // als konkrete document_types gibt es Offer, Order, DeliveryNote, Invoice (keine Gutschrift!)

const FLD_ID = 'id';
const FLD_DOCUMENT_LANGUAGE = 'document_language';
const FLD_DOCUMENT_CATEGORY = 'document_category'; // keyfield - per default "standard". brauchen wir z.B. zum filtern, zur Auswahl von Textbausteinen, Templates etc.
const FLD_DOCUMENT_NUMBER = 'document_number'; // kommt aus incrementable, in config einstellen welches incrementable fuer dieses model da ist!
const FLD_PRECURSOR_DOCUMENTS = 'precursor_documents'; // virtual, link
public const FLD_ID = 'id';
public const FLD_DOCUMENT_LANGUAGE = 'document_language';
public const FLD_DOCUMENT_CATEGORY = 'document_category'; // keyfield - per default "standard". brauchen wir z.B. zum filtern, zur Auswahl von Textbausteinen, Templates etc.
public const FLD_DOCUMENT_NUMBER = 'document_number'; // kommt aus incrementable, in config einstellen welches incrementable fuer dieses model da ist!
public const FLD_PRECURSOR_DOCUMENTS = 'precursor_documents'; // virtual, link

const FLD_BOILERPLATES = 'boilerplates';
const FLD_CUSTOMER_ID = 'customer_id'; // Kunde(Sales) (Optional beim Angebot, danach required). denormalisiert pro beleg, denormalierungs inclusive addressen, exklusive contacts
const FLD_CONTACT_ID = 'contact_id'; // Kontakt(Addressbuch) per default AP Extern, will NOT be denormalized
public const FLD_BOILERPLATES = 'boilerplates';
public const FLD_CUSTOMER_ID = 'customer_id'; // Kunde(Sales) (Optional beim Angebot, danach required). denormalisiert pro beleg, denormalierungs inclusive addressen, exklusive contacts
public const FLD_CONTACT_ID = 'contact_id'; // Kontakt(Addressbuch) per default AP Extern, will NOT be denormalized
// TODO FIXME denormalized.... as json in the document or as copy in the db?
const FLD_RECIPIENT_ID = 'recipient_id'; // Adresse(Sales) -> bekommt noch ein. z.Hd. Feld(text). denormalisiert pro beleg. muss nicht notwendigerweise zu einem kunden gehören. kann man aus kontakt übernehmen werden(z.B. bei Angeboten ohne Kunden)
public const FLD_RECIPIENT_ID = 'recipient_id'; // Adresse(Sales) -> bekommt noch ein. z.Hd. Feld(text). denormalisiert pro beleg. muss nicht notwendigerweise zu einem kunden gehören. kann man aus kontakt übernehmen werden(z.B. bei Angeboten ohne Kunden)

const FLD_DOCUMENT_TITLE = 'document_title';
const FLD_CUSTOMER_REFERENCE = 'customer_reference'; // varchar 255
const FLD_DOCUMENT_DATE = 'date'; // Belegdatum NICHT Buchungsdatum, das kommt noch unten
const FLD_PAYMENT_METHOD = 'payment_method'; // Sales_Model_PaymentMethod KeyField
const FLD_POSITIONS = 'positions'; // virtuell recordSet
public const FLD_DOCUMENT_TITLE = 'document_title';
public const FLD_CUSTOMER_REFERENCE = 'customer_reference'; // varchar 255
public const FLD_DOCUMENT_DATE = 'date'; // Belegdatum NICHT Buchungsdatum, das kommt noch unten
public const FLD_PAYMENT_METHOD = 'payment_method'; // Sales_Model_PaymentMethod KeyField
public const FLD_POSITIONS = 'positions'; // virtuell recordSet

const FLD_POSITIONS_NET_SUM = 'positions_net_sum';
const FLD_POSITIONS_DISCOUNT_SUM = 'positions_discount_sum';
public const FLD_POSITIONS_NET_SUM = 'positions_net_sum';
public const FLD_POSITIONS_DISCOUNT_SUM = 'positions_discount_sum';

const FLD_INVOICE_DISCOUNT_TYPE = 'invoice_discount_type'; // PERCENTAGE|SUM
const FLD_INVOICE_DISCOUNT_PERCENTAGE = 'invoice_discount_percentage'; // automatische Berechnung je nach tupe
const FLD_INVOICE_DISCOUNT_SUM = 'invoice_discount_sum'; // automatische Berechnung je nach type
public const FLD_NET_SUM = 'net_sum';

const FLD_NET_SUM = 'net_sum';
public const FLD_SALES_TAX = 'sales_tax';
public const FLD_SALES_TAX_BY_RATE = 'sales_tax_by_rate';
public const FLD_GROSS_SUM = 'gross_sum';

const FLD_SALES_TAX = 'sales_tax';
const FLD_SALES_TAX_BY_RATE = 'sales_tax_by_rate';
const FLD_GROSS_SUM = 'gross_sum';
public const FLD_COST_CENTER_ID = 'cost_center_id';
public const FLD_COST_BEARER_ID = 'cost_bearer_id'; // ist auch ein cost center

const FLD_COST_CENTER_ID = 'cost_center_id';
const FLD_COST_BEARER_ID = 'cost_bearer_id'; // ist auch ein cost center
public const FLD_NOTE = 'note';

const FLD_NOTE = 'note';

const FLD_BOOKING_DATE = 'booking_date'; // ggf. nur bei Rechnung ud. Storno
public const FLD_BOOKING_DATE = 'booking_date'; // ggf. nur bei Rechnung ud. Storno

// <dokumentenart>_STATUS z.B. Rechnungsstatus (Ungebucht, Gebucht, Verschickt, Bezahlt)
// übergänge haben regeln (siehe SAAS mechanik)

// OFFER:
// - OFFER_STATUS // keyfield: In Bearbeitung(ungebucht, offen), Zugestellt(gebucht, offen), Beauftragt(gebucht, offen), Abgelehnt(gebucht, geschlossen)

// ORDER:
// - INVOICE_RECIPIENT_ID // abweichende Rechnungsadresse, RECIPIENT_ID wenn leer
// - INVOICE_CONTACT_ID // abweichender Rechnungskontakt, CONTACT_ID wenn leer
// - INVOICE_STATUS // // keyfield: offen, gebucht; berechnet sich automatisch aus den zug. Rechnungen
// - DELIVERY_RECIPIENT_ID // abweichende Lieferadresse, RECIPIENT_ID wenn leer
// - DELIVERY_CONTACT_ID // abweichender Lieferkontakt, CONTACT_ID wenn leer
// - DELEVERY_STATUS // keyfield: offen, geliefert; brechnet sich automatisch aus den zug. Lieferungen
// - DELIVERY_STATUS // keyfield: offen, geliefert; brechnet sich automatisch aus den zug. Lieferungen
// pro position:
// - 1:n lieferpositionen (verknüpfung zu LS positionen)
// - zu liefern (automatisch auf anzahl, kann aber geändert werden um anzahl für erzeugten LS zu bestimmen)
Expand Down
53 changes: 41 additions & 12 deletions tine20/Sales/Model/Document/Offer.php
Expand Up @@ -21,6 +21,15 @@ class Sales_Model_Document_Offer extends Sales_Model_Document_Abstract
public const TABLE_NAME = 'sales_document_offer';

public const FLD_ORDER_ID = 'order_id';
public const FLD_OFFER_STATUS = 'offer_status';

/**
* offer status
*/
public const STATUS_IN_PROCESS = 'IN-PROCESS';
public const STATUS_DELIVERED = 'DELIVERED';
public const STATUS_ORDERED = 'ORDERED';
public const STATUS_REJECTED = 'REJECTED';

/**
* @param array $_definition
Expand Down Expand Up @@ -56,25 +65,45 @@ public static function inheritModelConfigHook(array &$_definition)
]],
];

// on offers customers are optional
self::_adaptFields($_definition);
}

/**
* @param array $_definition
* @return void
*/
protected static function _adaptFields(array &$_definition)
{
// offer customers are optional
unset($_definition[self::FIELDS][self::FLD_CUSTOMER_ID][self::VALIDATORS]);

// offers dont have precursor documents, that would be a crm lead or something in the future
// offers don't have precursor documents, that would be a crm lead or something in the future
// TODO for the FE, maybe we make this a virtual field? not present in DB, always of value null?
unset($_definition[self::FIELDS][self::FLD_PRECURSOR_DOCUMENTS]);

$_definition[self::FIELDS][self::FLD_ORDER_ID] = [
self::TYPE => self::TYPE_RECORD,
self::DISABLED => true,
self::CONFIG => [
self::APP_NAME => Sales_Config::APP_NAME,
self::MODEL_NAME => Sales_Model_Document_Order::MODEL_NAME_PART,
],
self::NULLABLE => true,
];

$_definition[self::FIELDS][self::FLD_POSITIONS][self::CONFIG][self::MODEL_NAME] =
Sales_Model_DocumentPosition_Offer::MODEL_NAME_PART;

$_definition[self::FIELDS] = array_merge($_definition[self::FIELDS], [
self::FLD_ORDER_ID => [
self::TYPE => self::TYPE_RECORD,
self::DISABLED => true,
self::CONFIG => [
self::APP_NAME => Sales_Config::APP_NAME,
self::MODEL_NAME => Sales_Model_Document_Order::MODEL_NAME_PART,
],
self::NULLABLE => true,
],
// OFFER_STATUS keyfield: In Bearbeitung(ungebucht, offen), Zugestellt(gebucht, offen),
// Beauftragt(gebucht, offen), Abgelehnt(gebucht, geschlossen)
self::FLD_OFFER_STATUS => [
self::LABEL => 'Status', // _('Status')
self::TYPE => self::TYPE_KEY_FIELD,
self::NAME => Sales_Config::DOCUMENT_OFFER_STATUS,
self::LENGTH => 255,
self::NULLABLE => true,
],
]);
}

/**
Expand Down

0 comments on commit a6b54b5

Please sign in to comment.