diff --git a/tests/tine20/Sales/Document/JsonTest.php b/tests/tine20/Sales/Document/JsonTest.php index ee9bff1896f..7492e7517c8 100644 --- a/tests/tine20/Sales/Document/JsonTest.php +++ b/tests/tine20/Sales/Document/JsonTest.php @@ -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)); diff --git a/tine20/Sales/Config.php b/tine20/Sales/Config.php index f7182b1cb56..9be246887ed 100644 --- a/tine20/Sales/Config.php +++ b/tine20/Sales/Config.php @@ -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 * @@ -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', diff --git a/tine20/Sales/Model/Document/Abstract.php b/tine20/Sales/Model/Document/Abstract.php index 58b3b191cb5..5d4f0dfe6c5 100644 --- a/tine20/Sales/Model/Document/Abstract.php +++ b/tine20/Sales/Model/Document/Abstract.php @@ -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 // _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) diff --git a/tine20/Sales/Model/Document/Offer.php b/tine20/Sales/Model/Document/Offer.php index 258814fdb56..41d02f2958c 100644 --- a/tine20/Sales/Model/Document/Offer.php +++ b/tine20/Sales/Model/Document/Offer.php @@ -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 @@ -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, + ], + ]); } /**