diff --git a/.travis.yml b/.travis.yml index 5d15e8843dc..63e48a051f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,6 @@ addons: - pgloader php: -- '5.4' - '5.5' - '5.6' - '7.0' @@ -62,8 +61,6 @@ matrix: - php: nightly # We exclude some combinations not usefull to save Travis CPU exclude: - - php: '5.5' - env: DB=mariadb - php: '5.6' env: DB=mariadb - php: '7.0' @@ -72,8 +69,6 @@ matrix: env: DB=mariadb - php: '7.2' env: DB=mariadb - - php: '5.5' - env: DB=postgresql - php: '5.6' env: DB=postgresql - php: '7.0' diff --git a/COPYRIGHT b/COPYRIGHT index cb1dc3e0d29..510bf5d3b4f 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -14,7 +14,7 @@ Component Version License GPL Compatible PHP libraries: AdoDb-Date 0.36 Modified BSD License Yes Date convertion (not into rpm package) ChromePHP 4.1.0 Apache Software License 2.0 Yes Return server log to chrome browser console -CKEditor 4.6.2 LGPL-2.1+ Yes Editor WYSIWYG +CKEditor 4.11.4 LGPL-2.1+ Yes Editor WYSIWYG PHPDebugBar 1.15.0 MIT License Yes Used only by the module "debugbar" for developers EvalMath 1.0 BSD Yes Safe math expressions evaluation Escpos-php ? MIT License Yes Thermal receipt printer library, for use with ESC/POS compatible printers diff --git a/ChangeLog b/ChangeLog index 3c9576e7544..355d9baa561 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,20 +2,24 @@ English Dolibarr ChangeLog -------------------------------------------------------------- + ***** ChangeLog for 10.0.0 compared to 9.0.0 ***** For Users: -NEW: Module Ticket is available as a stable module. +NEW: Module "Ticket" is available as a stable module. +NEW: module "Email Collector" is available as a stable module. +NEW: module "TakePOS" is available as a stable module. NEW: Experimental module "Vendor receptions". NEW: Experimental module "BOM". -FIX: Disallow line start date to be after end date +FIX: Disallow line start date if after end date. For Developers: -NEW: Module DebugBar is available as a stable module. +NEW: Module "DebugBar" is available as a stable module. WARNING: Following changes may create regressions for some external modules, but were necessary to make Dolibarr better: +* PHP 5.4 is no more supported. Minimum PHP is now 5.5+. * The PHP extension php-intl is not mandatory but should be installed to have new features working correctly. * Method GetUrlTrackingStatus were renamed into getUrlTrackingStatus for consistency with naming rules. * API getListOfCivility has been renamed into getListOfCivilities for consistency with naming rules. @@ -23,13 +27,98 @@ Following changes may create regressions for some external modules, but were nec * Files for variables of themes were renamed from graph-color.php into theme_vars.inc.php to match naming convention of extension .inc.php for files to be included. * All methods set_draft() were renamed into setDraft(). +* Signatures of methods createFromClone() has been standardized. All methods requires the object User as first parameter. * Removed deprecated function function test_sql_and_script_inject that was replaced with testSqlAndScriptInject. * Method load_measuring_units were renamed into selectMeasuringUnits and select_measuring_units was deprecated. * Hidden option CHANGE_ORDER_CONCAT_DESCRIPTION were renamed into MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION. * Method dolEscapeXML was moved from functions.lib.php into function2.lib.php (not used enough to be loaded by default). * Removed deprecated use of string in dol_print_date(). Only date allowed. +* Deprecated property ->fk_departement is now ->state_id everywhere. +* Removed the method 4 of GETPOST (to get $_COOKIE). It was not used and not recommanded to use in Dolibarr. +***** ChangeLog for 9.0.3 compared to 9.0.2 ***** +FIX: #11013 +FIX: #11041 +FIX: actioncomm: sort events by date after external calendars and hook (into 7.0) +FIX: better test +FIX: Combo list was limited to 20 in stock correction +FIX: Confusion between expired and late +FIX: Cursor pointer in payment screen for autofill +FIX: CVE-2019-11199 +FIX: CVE-2019-11200 +FIX: CVE-2019-11201 +FIX: Default value on form to send email +FIX: error messages not displayed +FIX: Massive debug in lettering function +FIX: missing compatibility with multicompany +FIX: missing global $user +FIX: missing situation invoice in list +FIX: MultiEntity in lettering functionality +FIX: Product accountancey sell intra code must be visible if main feature level 1 +FIX: ref for table without ref manager are set to NULL. +FIX: Sending email to mass actions send same email on same customer +FIX: Several fixes on import of services/products +FIX: shipping default warehouse if only one warehouse +FIX: sortfield on lettering function +FIX: Status of opportunity should never be -1 +FIX: test to display create invoice button on supplier_order card +FIX: The autocopy feature was ko for suppliers +FIX: Total per day in timespent per week +FIX: Total per day shows 00:00 if the total time spent is equal to 12:00 +FIX: Update/delete currency on same languages +FIX: Wrong variable name make contact of supplier order not used on PDF. +FIX: Add hidden option MAIN_PDF_HIDE_SITUATION to hide situation (quick hack to fix output pb). + +***** ChangeLog for 9.0.2 compared to 9.0.1 ***** +FIX: #10822 +FIX: Accountancy - Format EBP import +FIX: A page of a site replaced with another when switching in edit mode +FIX: Autodetect buy price for invoices autogenerated with templates. +FIX: Avoid error 500 when extension php-intl not loaded +FIX: bad check on type of expense report (mandatory status not working) +FIX: Bad label of status for members (must be short version in list) +FIX: Can not create contract with numbering module without autogen rule +FIX: Can't set default value of extrafield of type varchar +FIX: check only if invoice module is enabled (bank is check after) +FIX: counter of permissions in badge was wrong +FIX: default value of language of thirdparty +FIX: Don't show accountingjournal:getNomUrl without data +FIX: Duplicate executeHook function +FIX: Edit of personalized groups +FIX: Error with various & salary payment on project +FIX: extrafields always visible on view mode +FIX: function not found +FIX: If we build one invoice for several orders, we must put the ref of + orders on lines. +FIX: expensereport must be in $check array +FIX: missing entity filter and wrong var name +FIX: Missing field "In sale" in list +FIX: missing hook completeTabsHead in margins module +FIX: missing hook in agenda export +FIX: missing vat_src_code when inserting an expense report line +FIX: More complete auto setup of barcode module +FIX: need to round with 2 decimals to avoid movements not correctly balanced +FIX: no need to test anything to display documents tabs on expense report +FIX: old export models was not visible +FIX: Param keepn must be 1 when dol_escape_htmltag used for textarea +FIX: possibility to set up payment mode when invoice module is disabled +FIX: problem with sign of various payment in project preview +FIX: Remane of project +FIX: setup of module export +FIX: several hooks in shipping/delivery cards +FIX: supplier discount was not retrieved when choosing a product +FIX: The minimum amount filter does not work in the VAT report per customer +FIX: Tooltip on click was ko on smartphone +FIX: translation +FIX: useless join +FIX: Vat src code lost after editing expense report line +FIX: we need to keep originline special_code +FIX: Can't insert if there is extrafields mandatory on another entity. +FIX: error in create object when 2 extra fields are mandatory in 2 different entities +FIX: when we create deposit with multi tva, we mustn't add line if amount = 0 (example when we have a 100% reduc on one of origin invoice line) +FIX: wrong redirect link on holiday refuse +NEW: Add more complete error messages in log on stripe payments ***** ChangeLog for 9.0.1 compared to 9.0.0 ***** FIX: #10381 @@ -93,7 +182,7 @@ NEW: Experimental module "Data Privacy" NEW: Experimental module "Email Collector" NEW: Dolibarr can provide information in page title when multicompany is enabled of not, making Android application like DoliDroid able to provide native features for multicompany module. -NEW: Compatibility with PHP 7.3 => +NEW: Compatibility with PHP 7.3 => NEW: Add admin page for modulebuilder NEW: Add civility in list of members. Close #9251 NEW: Add configuration to disable "customer/prospect" thirdparty type @@ -511,7 +600,7 @@ NEW: Filter export model is now by user NEW: Finish implementation of option PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES NEW: generalize use of button to create new element from list NEW: hidden conf AGENDA_NB_WEEKS_IN_VIEW_PER_USER to set nb weeks to show into per user view -NEW: hidden conf to assign category to thirparty that are neither customer nor prospect or supplier +NEW: hidden conf to assign category to thirdparty that are neither customer nor prospect or supplier NEW: hidden conf to set nb weeks to show into user view NEW: hidden option MAIN_DISABLE_FREE_LINES NEW: improve way of adding users/sales representative to thirdparty @@ -585,10 +674,10 @@ WARNING: Following changes may create regressions for some external modules, but were necessary to make Dolibarr better: * Remove old deprecated hook 'insertExtraFields'. Triggers must be used for action on CRUD events. * Hook 'maildao' was renamed into 'mail' into the method sendfile that send emails, and method was renamed from - 'doaction' into 'sendMail'. + 'doaction' into 'sendMail'. * Rename trigger CONTRACT_SERVICE_ACTIVATE into LINECONTRACT_ACTIVATE and CONTRACT_SERVICE_CLOSE into LINECONTRACT_CLOSE -* Remove triggers *_CLONE. The trigger CREATE with context 'createfromclone' is already called so this is +* Remove triggers *_CLONE. The trigger CREATE with context 'createfromclone' is already called so this is a duplicated feature. Cloning is not a business event, the business event is CREATE, so no trigger required. * PHP 5.3 is no more supported. Minimum PHP is now 5.4+ * Remove the old deprecated code of doActions and getInstanceDao in canvas. The doActions of standard hooks are @@ -597,10 +686,10 @@ Following changes may create regressions for some external modules, but were nec * The hook contaxt commcard has been renamed thirdpartycomm * The hook contaxt thirdpartycard has been renamed thirdpartycontact * Remove method Categorie:get_nb_categories() that was not used. -* Hook getnomurltooltip provide a duplicate feature compared to hook getNomUrl so all hooks getnomurltooltip +* Hook getnomurltooltip provide a duplicate feature compared to hook getNomUrl so all hooks getnomurltooltip are now replaced with hook getNomUrl. * The substitution key __CONTACTCIVNAME__ is no longer present, it has been replaced by __CONTACT_NAME_{TYPE}__ - where {TYPE} is contact type code (BILLING, SHIPPING, CUSTOMER, ... see contact type dictionnary). + where {TYPE} is contact type code (BILLING, SHIPPING, CUSTOMER, ... see contact type dictionnary). ***** ChangeLog for 7.0.5 compared to 7.0.4 ***** @@ -1149,16 +1238,16 @@ way to save data for final version has changed. Following changes may create regressions for some external modules, but were necessary to make Dolibarr better: * The methode "cloture" on contract were renamed into "closeAll". -* The method "is_erasable" of invoice return a value <= 0 if not erasable (value is meaning) instead of always 0. -* The substitution key for reference of objects is now __REF__ whatever is the object (it replaces __ORDERREF__, +* The method "is_erasable" of invoice return a value <= 0 if not erasable (value is meaning) instead of always 0. +* The substitution key for reference of objects is now __REF__ whatever is the object (it replaces __ORDERREF__, __PROPALREF__, ...) -* The substition key __SIGNATURE__ was renamed into __USER_SIGNATURE__ to follow naming conventions. +* The substition key __SIGNATURE__ was renamed into __USER_SIGNATURE__ to follow naming conventions. * Substitution keys with syntax %XXX% were renamed into __XXX__ to match others. * Removed old deprecated REST API (APIs found into '/root' section of the REST API explorer in Dolibarr v6). -* Some REST API to access setup features, like dictionaries (country, town, extrafields, ...) were moved into a +* Some REST API to access setup features, like dictionaries (country, town, extrafields, ...) were moved into a common API "/setup". -* The REST API /documents were renamed into /documents/download and /documents/upload. -* Page bank/index.php, bank/bankentries.php and comm/actions/listactions.php were renamed into +* The REST API /documents were renamed into /documents/download and /documents/upload. +* Page bank/index.php, bank/bankentries.php and comm/actions/listactions.php were renamed into bank/list.php, bank/bankentries_list.php and comm/actions/list.php to follow page naming conventions (so default filter/sort order features can also work for this pages). * The trigger ORDER_SUPPLIER_STATUS_ONPROCESS was renamed into ORDER_SUPPLIER_STATUS_ORDERED. @@ -1166,17 +1255,17 @@ Following changes may create regressions for some external modules, but were nec * The parameter note into method cloture() is added at end of private note (previously in v6, it replaced). * The parameter $user is now mandatory for method createFromOrder and createFromPropal. * Removed js library 'fileupload' that was not used by core code. -* Jquery plugin tableDnd updated. You now need to use decodeURI on the return value of tableDnDSerialize() - and add 'td.' to the beginning of the dragHandle match string. -* IE8 and earlier and Firefox 12 and earlier (< 2012) are no more supported. -* The module ExpenseReport use numbering rules that you can setup (like other modules do). If you need to +* Jquery plugin tableDnd updated. You now need to use decodeURI on the return value of tableDnDSerialize() + and add 'td.' to the beginning of the dragHandle match string. +* IE8 and earlier and Firefox 12 and earlier (< 2012) are no more supported. +* The module ExpenseReport use numbering rules that you can setup (like other modules do). If you need to keep the hard coded numbering rule of expenses report used in 6.0, just add constant EXPENSEREPORT_USE_OLD_NUMBERING_RULE to 1. * If you use the external module "multicompany", you must also upgrade the module. Multicompany module for Dolibarr v7 is required because with Dolibarr v7, payment modes and payment conditions are management as data that are dedicated to each company. If you keep your old version of multicompany module, mode and - condition of payments will appears empty in all companies that are not the first one. By upgrading the - multicompany module to a version that support Dolibarr v7, everything should work as expected. + condition of payments will appears empty in all companies that are not the first one. By upgrading the + multicompany module to a version that support Dolibarr v7, everything should work as expected. ***** ChangeLog for 6.0.8 compared to 6.0.7 ***** @@ -1257,7 +1346,7 @@ NEW: field commerciaux and categ export CustomersInvoicesAndPayments ***** ChangeLog for 6.0.5 compared to 6.0.4 ***** FIX: security vulnerability reported by ADLab of Venustech - CVE-2017-17897, CVE-2017-17898, CVE-2017-17899, CVE-2017-17900 + CVE-2017-17897, CVE-2017-17898, CVE-2017-17899, CVE-2017-17900 FIX: #7379: Compatibility with PRODUCT_USE_OLD_PATH_FOR_PHOTO variable FIX: #7903 FIX: #7933 @@ -1398,11 +1487,11 @@ FIX: Upgrade missing on field FIX: View of timespent for another user FIX: ODT generation FIX: CVE-2017-9840, CVE-2017-14238, CVE-2017-14239, CVE-2017-14240, CVE-2017-14241, - CVE-2017-14242 - + CVE-2017-14242 + ***** ChangeLog for 6.0.0 compared to 5.0.* ***** NEW: Add experimental BlockeLog module (to log business events in a non reversible log file). -NEW: Add a payment module for Stripe. +NEW: Add a payment module for Stripe. NEW: Add module "Product variant" (like red, blue for the product shoes) NEW: Accountancy - Activate multi-journal & Add journal_label to database (FEC) NEW: Add a tracking id into mass emailing. @@ -1428,8 +1517,8 @@ NEW: Can add a background image on login page NEW: Can change customer from POS NEW: Can clone expense report on another user NEW: Can control constants values into file integrity checker -NEW: Can define default values for create forms. -NEW: Can define default filters for list pages. +NEW: Can define default values for create forms. +NEW: Can define default filters for list pages. NEW: Can define default sort order for list pages. NEW: Can deploy an external module from the module setup area. NEW: Can disable all overwrote translations in one click. @@ -1523,16 +1612,16 @@ NEW: Removed commande_pdf_create, contract_pdf_create,expedition_pdf_create, fac NEW: tooltip can be on hover or on click with textwithpicto function. NEW: Upgrade jquery to 3.3.1 and jquery-ui to 1.12 -WARNING: +WARNING: Following changes may create regression for some external modules, but were necessary to make Dolibarr better: * The hook getNodeList has been replaced by a normalized 'addreplace' hook getDirList. * The trigger USER_SETINGROUP and USER_REMOVEFROMGROUP has been replaced with trigger USER_MODIFY. * The page societe/soc.php was renamed into societe/card.php to match page naming conventions. * The page compta/facture.php was renamed into compta/facture/card.php to match page naming conventions. -* The signature of method ->delete() of class Product and PriceExpression was changed from +* The signature of method ->delete() of class Product and PriceExpression was changed from ->delete(id, notrigger) to ->delete(User, notrigger) to match standard dev rules. -* The signature of method ->delete() of class Adherent was changed from +* The signature of method ->delete() of class Adherent was changed from ->delete(id) to ->delete(id, User, notrigger) to match standard dev rules. * Removed CommonObject::displayMarginInfos (was deprecated in 3.8). Use same method into html.formmargin.class.php @@ -1540,14 +1629,14 @@ Following changes may create regression for some external modules, but were nece * Removed the trigger file of PAYPAL module that stored data that was not used by Dolibarr. The trigger event still exists, but if an external module need action on it, it must provides itself its trigger file. * Use $conf->global->MULTICOMPANY_TRANSVERSE_MODE instead $conf->multicompany->transverse_mode. So, if you set var - $multicompany_transverse_mode to 1 into your conf file, you must remove this line and a new key into + $multicompany_transverse_mode to 1 into your conf file, you must remove this line and a new key into the Home - setup - other admin page. * If you use Multicompany transverse mode, it will be necessary to check the activation of the modules in the children entities and to review completely the rights of the groups and the users. * Use getEntity('xxx') instead getEntity('xxx', 1) and use getEntity('xxx', 0) instead getEntity('xxx') -* Some other change were done in the way we read permission of a user when module multicompany is enabled. You can +* Some other change were done in the way we read permission of a user when module multicompany is enabled. You can retreive the old behavior by adding constant MULTICOMPANY_BACKWARD_COMPATIBILITY to 1. -* The hook formObjectOptions was not implemented correctly in previous version. Sometimes, you had to return output +* The hook formObjectOptions was not implemented correctly in previous version. Sometimes, you had to return output content by doing a print into function, sometimes by returning content into "resprint". This has been fixed to follow hook specifications so you must return output into "resprint". @@ -1709,11 +1798,11 @@ FIX: origin & origin id on supplier order line FIX: param php doc FIX: Picto of project on dol_banner and box FIX: Some errors when downloading files. - + ***** ChangeLog for 5.0.0 compared to 4.0.* ***** For users: NEW: Add module mulicurrency. -NEW: Add module accoutancy expert (double party accountancy). +NEW: Add module accoutancy expert (double party accountancy). NEW: Better responsive design, above all on smartphone. NEW: #5801 More complete change to allow to disable supplier invoice document generation. NEW: #5830 Can choose a generic email or use remail in the mail from field. @@ -1748,7 +1837,7 @@ NEW: Better filtering of automatic/manually inserted events. NEW: Bill orders from order list. NEW: Can add event from the card listing events. NEW: Can change thirdparty when cloning a project. -NEW: Can create expense report for someone else (advanced permission). +NEW: Can create expense report for someone else (advanced permission). NEW: Can clone an expense report. NEW: Can edit a label for each price segment when using several segment prices for products. NEW: Can filter on fields on admin translation page. @@ -1833,7 +1922,7 @@ NEW: Hook on stock product card NEW: param socid find_min_price_product_fournisseur() function NEW: More phpunit tests -WARNING: +WARNING: Following changes may create regression for some external modules, but were necessary to make Dolibarr better: @@ -1847,10 +1936,10 @@ Dolibarr better: Method warehouse->delete(id) has been replace with ->delete(user) This is to follow good practice to make a fetch on object before deleting it. - The form to add a product to a draft proposal/order/invoice, from the product card, is hidden by default. - It was not commonly used and usage generates some problems (cost price for margin calculation not entered, vat setting). + It was not commonly used and usage generates some problems (cost price for margin calculation not entered, vat setting). Set constant PRODUCT_ADD_FORM_ADD_TO to retrieve it. - The javascript "datatables" library was previously provided into Dolibarr sources, but it was not used by application. - So there is no reason to maintain its compatibility with other dolibarr components. If an external module need this + So there is no reason to maintain its compatibility with other dolibarr components. If an external module need this library, this external module must embed the library in his own sources/packages. - Trigger name SUPPLIER_PROPOSAL_CREATE has been renamed into PROPOSAL_SUPPLIER_CREATE. - A new paramater sqlfilters was introduced to allow filter on any fields int the REST API. Few old parameters, @@ -1938,7 +2027,7 @@ FIX: repair tool was ko to restore extrafields with type select. FIX: Security access problem with external users on projects/tasks FIX: We must not drop extrafield column if there is still record on other entities. FIX: regression with sedning email when introducing security options to restrict nb of email sending. -t +t ***** ChangeLog for 4.0.3 to 4.0.2 ***** FIX: #5853 $conf->global->$calc==0 || $conf->global->$calc==1 FIX: #5958 no discount on supplier command made by replenishment @@ -2050,7 +2139,7 @@ For users: NEW: Add recurring invoice feature and automatic generation of invoices. NEW: Add module "Loan" as stable. NEW: Add module "Supplier commercial proposal" (price request) with stable status. -NEW: Can select dynamicaly number of lines to show on page on product, shipment, contact, orders, thirdparties. +NEW: Can select dynamicaly number of lines to show on page on product, shipment, contact, orders, thirdparties. NEW: Can select fields to show on list also for list of customer orders, supplier orders, shipments, proposals and invoices. NEW: Show into badge on tab head, the number of dedicated contacts for all objects. NEW: Add a checkbox to select/unselect all lines on page that support mass actions (like invoice list page) @@ -2060,15 +2149,15 @@ NEW: Add date_rum into table of thirdparty bank account. NEW: The probability of lead/opportunity can be defined per lead. NEW: Added Malta VAT into migration script NEW: Add Expense report into accountancy report -NEW: Add Expense report to approve into workboard +NEW: Add Expense report to approve into workboard NEW: Selection of boxes is moved on top of home page NEW: Add filter on a keyword, status and nature into list of modules. NEW: Add hidden option BANK_DISABLE_CHECK_DEPOSIT to disable check deposit feature. NEW: Add hidden option MAIN_PUBLIC_NOTE_IN_ADDRESS -NEW: Add index on invoice status +NEW: Add index on invoice status NEW: Add constant MAIN_LOGTOHTML to 0 into setup by default to save time when we need to make debug on hosted instance. NEW: Add list of billed -NEW: Add minimum stock and desired stock into import/export profiles. +NEW: Add minimum stock and desired stock into import/export profiles. NEW: Add state into thirdparty export fields. NEW: Add more trackable events (create, submit and receive supplier order). NEW: Add hidden option MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN @@ -2080,7 +2169,7 @@ NEW: Add statistics on number of projets on home page NEW: Add statistics and late records into dashboard for supplier proposals. NEW: Add the admin info on combo of type of contact NEW: Add the event BILL_PAYED to the list of supported events for module notification. -NEW: Add total weight and volume on PDF. +NEW: Add total weight and volume on PDF. NEW: Add hidden option to hide column qty ordered on shipments. NEW: Add view of virtual stock into product list (when appropriate) NEW: Add warning on tasks when they are late (add also the warning tolerance parameter) @@ -2154,7 +2243,7 @@ NEW: TimeZone can be supplied to mktime NEW: hook in shipment card NEW: Deprecated Societe::set_prospect_level, Societe::set_commnucation_level, Societe::set_OutstandingBill functions NEW: A module can add, into its import profiles, a sql request to execute at end of import. This allow to update dernormalized data after import. -NEW: Add hook pdf_build_address +NEW: Add hook pdf_build_address NEW: Add a parameter on graph function to show a generic graph when no data are available. NEW: Add $object in parameter of pdf_build_address so we could include hook into the function. NEW: Add a tool for developers to purge database with no loose of setup @@ -2173,7 +2262,7 @@ NEW: More phpunit tests. Include some REST API into automatic tests. NEW: Move Expense report menu from module to menu files. -WARNING: +WARNING: Dolibarr 4.0 should be compatible with PHP 7 but more feedbacks are still expected to confirm that. @@ -2182,7 +2271,7 @@ Dolibarr better: - Function log() of class CommandeFournisseur has been removed. Using it is no more required. - Class Resource was renamed into DolResource to avoid conflict with a reserved PHP word. - Method commonobject->add_thumb() has been renamed into commonobject->addThumbs(). -- Method select_type_comptes_financiers() has been renamed into selectTypeOfBankAccount() +- Method select_type_comptes_financiers() has been renamed into selectTypeOfBankAccount() - Property ->client that was deprecated 6 years ago, is replaced in all core code with ->thirdparty. - File '/core/tpl/document_actions_pre_headers.tpl.php' were renamed into '/core/actions_linkedfiles.inc.php'. So if you included it into your module, change your code like this to be compatible with all version: @@ -2350,7 +2439,7 @@ FIX: end of select when no fournprice FIX: Filter on assigned to was preselected on current user on list "All events" (instead of no filtering) FIX: Filter on category tag for suppliers FIX: hook on group card called but not initialized -FIX: Infinite loop on menu tree output for edition +FIX: Infinite loop on menu tree output for edition FIX: Can show tree of entries added by external modules using fk_mainmenu and fk_leftmenu instead of fk_menu. FIX: init var at wrong place report incorrect "shippable" flag on draft order. FIX: It doesn't check if there is enough stock to update the lines of orders/invoices @@ -2442,7 +2531,7 @@ FIX: When using option Price per level, when adding a predefined product, the va ***** ChangeLog for 3.9.0 compared to 3.8.* ***** For users: NEW: A new and more modern look for "eldy" theme. -NEW: Introduce a new theme called "Material Design". +NEW: Introduce a new theme called "Material Design". NEW: #3767 Allow changing multiple prices of a product at once NEW: Add a button to purge criteria in user list NEW: Add a filter field to restrict list of member when doing a LDAP list request. Use also this filter into ldap command line script making sync from ldap to dolibarr. @@ -2492,7 +2581,7 @@ NEW: Can set default value of event type when creating an event (if option "mana NEW: Can upload files on leave requests. Use more standard permissions. NEW: Can use a "|" to make a OR search on several different criterias into search text filters of tables. NEW: Can use the * as a joker characters into search boxes of lists. -NEW: Clean code into salary module, debug and add indexes +NEW: Clean code into salary module, debug and add indexes NEW: Can filter on user list and salary payments on user with naural search. NEW: Can clone agenda events. NEW: Color category is visible onto the thumb of tags on thirdparty, or products cards. @@ -2526,7 +2615,7 @@ NEW: Print event type on third party card tab agenda list (only if AGENDA_USE_EV NEW: Provide an easier way to understand if an order can be shipped. NEW: Quick search filter works also on invoice, proposal, order, intervention, contract and expense reports. NEW: Replace category edition page on members with new select2 component. -NEW: Show photo of logged user into login top right block. +NEW: Show photo of logged user into login top right block. NEW: If no photo is available for user, we show a generic photo depending on gender. NEW: Show photo of user into user list. NEW: Show which fields were used for search when doing a "generic search" from the "quick search" form on left menu. @@ -2539,7 +2628,7 @@ NEW: The thirdparties tabs, the contacts tabs and the members tabs are now prese NEW: Thumbs for statistics on main page are fully clicable (not only link inside the thumb) NEW: Translate extrafield's labels. NEW: Use new select2 component for juridical status, country and state selection. -NEW: When creating order, proposal or invoice from thirdparty card, the project is asked during creation. A link to create project if it does not exists is also available. +NEW: When creating order, proposal or invoice from thirdparty card, the project is asked during creation. A link to create project if it does not exists is also available. NEW: Uniformize form creation of proposal to add public and private notes during creation like for order and invoice. NEW: More robust antiXSS engine. NEW: Compatibility with Mysql 5.7+ @@ -2573,13 +2662,13 @@ NEW: ODT generators can now also set meta properties of ODT file. NEW: Add missing columns into llx_expedition to match other tables. NEW: A new function getImageFileNameForSize was also introduced to choose image best size according to usage to save bandwith. NEW: Support logging to a Sentry server -NEW: Prepare database to have agenda able to store more detailed emails events. +NEW: Prepare database to have agenda able to store more detailed emails events. -WARNING: +WARNING: Dolibarr 3.9 is not yet fully compatible with PHP 7 even if most features seems to work. -Mysql minimum version is now 5.0.3 +Mysql minimum version is now 5.0.3 Following changes may create regression for some external modules, but were necessary to make Dolibarr better: @@ -2587,7 +2676,7 @@ Dolibarr better: - Deprecated hidden option MAIN_USE_CUSTOM_TRANSLATION has been removed. Use table llx_overwrite_trans instead. - Trigger LINECONTRACT_INSERT has been renamed into LINECONTRACT_CREATE to match common denomination. - A lot hooks used into PDF generation were not correctly implemented. We had to fix this. The result si that -the following hook were set as hook of type "replace". This means if your module implement such hooks, it must +the following hook were set as hook of type "replace". This means if your module implement such hooks, it must return 0 to execute standard code or 1 to replace standard code (value to output should be set into resPrints instead). This is list of hooks modified: 'pdf_getlinenum', 'pdf_getlineref', 'pdf_getlineref_supplier', 'pdf_getlinevatrate', 'pdf_getlineupexcltax', @@ -2754,7 +2843,7 @@ FIX: the view my task must show only task you are assigned to FIX: to allow phpunit of migration process for 3.4 to 3.5 FIX: to allow phpunit of migration process for 3.5 to 3.6 FIX: userlocaltax -FIX: view of product image when using old path +FIX: view of product image when using old path FIX: size of image uploaded on user. FIX: We must ue the "small" size of imge to show on card pages. FIX: When we make a direct assignement on a task to a user, we must check he is also assigned to project (and if not assign it) @@ -3171,13 +3260,13 @@ NEW: Introduce function dolGetFirstLineOfText. WARNING: Following changes may create regression for some external modules, but were necessary to make Dolibarr better: -- Removed hook supplierorderdao into supplier order creation. This is a business event, so we must use the +- Removed hook supplierorderdao into supplier order creation. This is a business event, so we must use the trigger ORDER_SUPPLIER_CREATE instead. - Hooks 'printLeftBlock' and 'formConfirm' are now compliant with hook development rules. They are - "addreplace" hooks, so you must return content with "->resprints='mycontent'" and not with "return 'mycontent'" + "addreplace" hooks, so you must return content with "->resprints='mycontent'" and not with "return 'mycontent'" - All fields "fk_societe" and "fk_soc" are now named "fk_soc" (same name for all fields). - Method select_PriceBaseType and load_PriceBaseType were merged into selectPriceBaseType. -- The triggers USER_LOGIN* are deprecated. They are still working but you should prefer use the +- The triggers USER_LOGIN* are deprecated. They are still working but you should prefer use the hook afterLogin or afterLoginFailed instead. - The trigger USER_CREATE_FROM_CONTACT has been replace with USER_CREATE and property context is now filled to make difference between creation from contact or not. @@ -3510,7 +3599,7 @@ You may also experience troubles with Mysql 5.5.41 with error "Lost connection" Upgrading to any other version or database system is abolutely required BEFORE trying to make a Dolibarr upgrade. -WARNING: +WARNING: Following changes may create regression for some external modules, but was necessary to make Dolibarr better: @@ -3666,7 +3755,7 @@ For users: - Fix: Salaries payment - Field date value is now required and add control on it. - Fix: Iban was used instead of Bic into SEPA file. - Fix: Must unaccent strings into SEPA file. -- Fix: Extrafield feature select from table should try to translate multiple column when not needed +- Fix: Extrafield feature select from table should try to translate multiple column when not needed - Fix: cents for indian ruppes are called paisa and paise. - Fix: Invoices payments may be older than invoices. - Fix: Withdrawal total amount is double @@ -3705,7 +3794,7 @@ For users: note of generated documents. - New: Add warning if supplier payment is higher that due amount. - New: Increase length of url into bookmark module. -- New: Automatic events sending mails add info about linked objects into email content. +- New: Automatic events sending mails add info about linked objects into email content. - New: Price management enhancement (multiprice level, price by customer, if MAIN_FEATURES_LEVEL=2 Price by qty). - New: Add option MAIN_FAVICON_URL. - New: Created {line_price_ht_locale}, {line_price_vat_locale} and {line_price_ttc_locale} ODT tags. @@ -3719,13 +3808,13 @@ For users: - New: Prepare generation of SEPA files into module withdrawal. - New: [ task #1164 ] Add "Ref. supplier" search box in supplier orders - New: [ task #1345 ] Can filter on status for supplier order. -- New: Add option FACTURE_SENDBYEMAIL_FOR_ALL_STATUS to allow to send invoice by email +- New: Add option FACTURE_SENDBYEMAIL_FOR_ALL_STATUS to allow to send invoice by email whatever is its status. - New: Add filter date in bank writing list page. - New: Extrafields can be used as substitution key %EXTRA_XXX% into emails texts for members. - New: Add categories translation. - New: Enable option "clone target emailing". -- New: Improved tax module: Add specific page for salaries payment +- New: Improved tax module: Add specific page for salaries payment - New: Add composer.json file so Dolibarr can be publish onto packagist.org. - New: The combo list of juridical status is now sorted - New: [ task #926 ] Add extrafield feature on order lines. @@ -3758,7 +3847,7 @@ For developers: - New: Add path file of trigger into admin trigger list page. - New: More phpunit tests. - New: Payments and supplier payment pages tabs can now be extended from modules. -- New: Add option 'aZ' into GETPOST function to check parameters contains +- New: Add option 'aZ' into GETPOST function to check parameters contains only a to z or A to Z characters. - New: Opensurvey polls tab cards can now be extended from external modules. - New: Triggers OPENSURVEY_CREATE, OPENSURVEY_DELETE added. @@ -3778,12 +3867,12 @@ WARNING: Following change may create regression for some external modules, but w Dolibarr better: - The deprecated way (with 4 parameters) to declare a new tab into a module descriptor file has been -removed. You must now use the 6 parameters way. See file modMyModule.class.php for example. +removed. You must now use the 6 parameters way. See file modMyModule.class.php for example. - Remove the javascript function ac_delay() that is not used anymore by core code. - Properties "dictionnaries" into module descriptor files have been renamed into "dictionaries". - Method form->select_currency() has been removed. Use instead print form->selectCurrency(). - Method form->select_methodes_commande() has been renamed into english name selectInputMethod(). -- The following hooks are now 'addreplace' hooks: "formCreateThirdpartyOptions" +- The following hooks are now 'addreplace' hooks: "formCreateThirdpartyOptions" So check that return value is 0 to keep default standard behaviour after hook, or 1 to disable default standard behaviour. - Properties "civilite_id" were renamed into "civility_id". @@ -3887,9 +3976,9 @@ Fix: The object deliverycompany was not used anymore and output of Fix: [ bug #1445 ] html fix : missing Fix: [ bug #1415 ] Intervention document model name and suppliers model names is not shown properly in module configuration -Fix: [ bug #1416 ] Supplier order does not list document models in the select box of the +Fix: [ bug #1416 ] Supplier order does not list document models in the select box of the supplier order card -Fix: [ bug #1443 ] Payment conditions is erased after editing supplier invoice label or +Fix: [ bug #1443 ] Payment conditions is erased after editing supplier invoice label or limit date for payment Fix: Filter on status was not visible when selected from url. Fix: Filtering on status was last when asking to sort. @@ -3898,7 +3987,7 @@ Fix: [ bug #1449 ] Trigger ORDER_CREATE, LINEORDER_DELETE, LINEORDER_UPDATE and Fix: [ bug #1450 ] Several Customer order's triggers do not report the error from the trigger handler. Fix: [ bug #1451 ] Interrupted order clone through trigger, loads nonexistent order. Fix: [ bug #1454 ] Mention de bas de page erroné -Fix: Do not display dictionary for non activated module +Fix: Do not display dictionary for non activated module Fix: Link element from element project pages Fix: [ bug #1509 ] Expedition admin free text & watermark submit error Fix: [ bug #1349 ] AJAX contact selector does not work fine in Project card @@ -3923,7 +4012,7 @@ Fix: [ bug #1351 ] VIES verification link broken. Fix: [ bug #1352 ] Removing a shipping does not remove the delivery. Fix: Option MAIN_INVERT_SENDER_RECIPIENT broken with typhon template. Fix: Can disable features with PHPEXCEL (no DLSF compatible). -Fix: Can disable features with CKEDITOR. +Fix: Can disable features with CKEDITOR. Fix: Pb of records not correctly cleaned when module marge is uninstalled (conflict between 'margin' and 'margins'). Fix: [ bug #1341 ] Lastname not added by file or direct input in mass e-mailing. @@ -3974,8 +4063,8 @@ Fix: When select_date is called with '' as preselected date, Fix: First param of select_date must always be forged with a dolibarr date function and not time(). Fix: fix can't add line with product in supplier order -Fix: [bug #1309] -Fix: Solve pb of too many embedded tables +Fix: [bug #1309] +Fix: Solve pb of too many embedded tables Fix: [ bug #1306 ] Fatal error when adding an external calendar Fix: A fix to manage automatic creation of code for import. Fix: Try to add code to provide easy way to fix warning on timezone not @@ -3983,7 +4072,7 @@ Fix: Try to add code to provide easy way to fix warning on timezone not Fix: Several fix into workflow/condition for invoice payments or convert into discount. Fix: Option MAIN_PDF_DASH_BETWEEN_LINES was not working when tcpdf was - making a pagebreak higher than 2 pages. + making a pagebreak higher than 2 pages. Fix: form to add images should not show link form. Fix: Correction when adding order line with price as '0'. Fix: [ bug #1283 ] ROUGET Shipment PDF. @@ -3995,7 +4084,7 @@ Fix: bug #1295: Error when creating an agenda extrafield with a number as refere Fix: Translation of number for pt_PT. Fix: Error on ajax_constantonoff function. Fix: [ bug #1323 ] problème pour générer un odt depuis les taches dans projet. -Fix: Can not make withdrawals +Fix: Can not make withdrawals ***** ChangeLog for 3.5.1 compared to 3.5.0 ***** Fix: Do not report trigger errors twice. @@ -4011,17 +4100,17 @@ Fix: Link to paypal was invalid into email text. Fix: ref and date of supplier invoice. Fix: Check on bank account. Fix: Problem with file upload and download. -Fix: Page load not ending when large number of thirdparties. We +Fix: Page load not ending when large number of thirdparties. We added option MAIN_DISABLE_AJAX_COMBOX to disable javascript combo feature that is root cause of problem. Fix: [ bug #1231 ] PDF always generated in interventions Fix: Be sure there is no duplicate default rib. Fix: Enable extrafields for customer order, proposal and invoice lines. This feature was developed for 3.5 but was disabled (hidden) because of a bug not possible to - fix enough quickly for 3.5.0 release. + fix enough quickly for 3.5.0 release. Fix: user right on Holiday for month report nor working. Fix: [ bug #1250 ] "Supplier Ref. product" sidebar search box does not work -Fix: Bad space in predefined messages. +Fix: Bad space in predefined messages. Fix: [ bug #1256 ] Signature was not added for email sent from thirdparty page. Fix: Action event SHIPPING_VALIDATE is not implemented Fix: The customer code was set to uppercase when using numbering module leopard. We @@ -4067,7 +4156,7 @@ For users: - New: Add a cron module to define scheduled jobs. - New: Add new graphical boxes (customer and supplier invoices and orders per month). - New: [ task #286 ] Enhance rounding function of prices to allow round of sum instead of sum of rounding. -- New: Can add an event automatically when a project is create. +- New: Can add an event automatically when a project is create. - New: Add option MAIN_GENERATE_DOCUMENT_WITH_PICTURE. - New: Add option excludethirdparties and onlythirdparties into merge pdf scripts. - New: [ task #925 ] Add ODT document generation for Tasks in project module. @@ -4089,7 +4178,7 @@ For users: - New : Add ability to copy contact address to clipboard. - New: Can use tag {mm} before {yy} even when there is a reset into numbering masks. - New: [ task #1060 ] Register fields localtax(1|2)_type into details tables. -- New: [ task #923 ] Localtax support for ODT templates. +- New: [ task #923 ] Localtax support for ODT templates. - New: [ task #90 ] Barcode search. - New: Add hidden option MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS. - New: Can send an email from thirdparty card. @@ -4141,26 +4230,26 @@ For developers: - New: A trigger can return an array of error strings instead of one error string. - New: Add method to use a dictionary as a combo box. - New: Add update method for web service product. -- Fix also several bugs with old code. +- Fix also several bugs with old code. WARNING: Following change may create regression for some external modules, but was necessary to make Dolibarr better: -1) We started to clean hooks code. -If your hook want to modify value of $actions, it's role of your hook to modify it. Dolibarr +1) We started to clean hooks code. +If your hook want to modify value of $actions, it's role of your hook to modify it. Dolibarr hook code will no more decide this for your module. If your action class for hook was returning -a string or an array, instead your module must set $actionclassinstance->results (to return array) -or $actionclassinstance->resprints (to return string) to return same thing. The return value must +a string or an array, instead your module must set $actionclassinstance->results (to return array) +or $actionclassinstance->resprints (to return string) to return same thing. The return value must be replaced by a "return 0"; -Goal is to fix old compatibility code that does not match hook specifications: - http://wiki.dolibarr.org/index.php/Hooks_system +Goal is to fix old compatibility code that does not match hook specifications: + http://wiki.dolibarr.org/index.php/Hooks_system 2) If you implemented hook printTopRightMenu, check that output does not include '' tags any more. All content added must be tagged by a '
' with css class="login_block_elem" 3) Some methods object->addline used a first parameter that was object->id, some not. Of course -this was not a good practice, since object->id is already known, there is no need to provide id as -parameter. All methods addline in this case were modified to remove this parameter. +this was not a good practice, since object->id is already known, there is no need to provide id as +parameter. All methods addline in this case were modified to remove this parameter. 4) Method ->classer_facturee() is deprecated. It must be replace with ->classifyBilled(). @@ -4218,7 +4307,7 @@ Fix: update extrafield do not display immediatly after update. Fix: Fix bug with canvas thirdparty. Fix: [ bug #1037 ] Consumption> Supplier invoices related. Fix: User group name do not display in card (view or edit mode). -Fix: Link "Show all supplier invoice" on suplier card not working. +Fix: Link "Show all supplier invoice" on suplier card not working. Fix: [ bug #1039 ] Pre-defined invoices conversion. Fix: If only service module is activated, it's impossible to delete service. Fix: [ bug #1043 ] Bad interventions ref numbering. @@ -4249,7 +4338,7 @@ For users: - New: Add hidden option MAIN_PDF_TITLE_BACKGROUND_COLOR. - New: Merge tab customer and prospect. - New: Add ES formated address country rule. -- New: Can define a hierarchical responsible on user and add a tree view to +- New: Can define a hierarchical responsible on user and add a tree view to see hierarchy of users. - New: Can expand/collapse menus, categories and users list. - New: extra parameters are supported into ODT/ODS templates. @@ -4281,7 +4370,7 @@ For users: - New: [ task #773 ] Add Project document in GED(ECM) modules. - New: [ task #783 ] Add more types for extra parameters (lists, phone, emails, checkbox, prices, radio). -- New: [ task #798 ] Add range limit date on product/services as it is done on order +- New: [ task #798 ] Add range limit date on product/services as it is done on order and invoice. - New: [ task #814 ] Add extrafield feature for projects ands tasks. - New: [ task #770 ] Add ODT document generation for Projects module. @@ -4298,12 +4387,12 @@ For translators: - Update language files. For developers: -- System of menu managers has been rewritten to reduce code to do same things. +- System of menu managers has been rewritten to reduce code to do same things. - An external module can force its theme. - Add function dol_set_focus('#xxx'). - A mymodule can bring its own core/modules/mymodule/modules_mymodule.php file. - Removed not used libraries. -- More web services. +- More web services. - Renamed some database fields, code variables and parameters from french to english. - First change to manage margins on contracts. - Add hook getFormMail. @@ -4311,25 +4400,25 @@ For developers: into conf->liste_limit). - New: Add option dol_hide_topmenu, dol_hide_leftmenu, dol_optimize_smallscreen, dol_no_mouse_hover and dol_use_jmobile onto login page (to support different terminal). -- New: dol_syslog method accept a suffix to use different log files for log. +- New: dol_syslog method accept a suffix to use different log files for log. - New: Type of fields are received by export format handlers. - New: when adding an action, we can define a free code to tag it for a specific need. -- New: Enhance Dolibarr migration process to include migration script of external +- New: Enhance Dolibarr migration process to include migration script of external modules. - New: [ task #811 ] Uniformanize note field. - + WARNING: If you used external modules, some of them may need to be upgraded due to: - Fields of classes were renamed to be normalized (nom, prenom, cp, ville, adresse, tel were renamed into lastname, firstname, zip, town, address, phone). This may also be true for some fields into web services. -- If module use hook pdf_writelinedesc, module may have to add return 1 at end of +- If module use hook pdf_writelinedesc, module may have to add return 1 at end of function to keep same behaviour. TODO: backport commit 53672dff75f4fdaeeed037ff9d15f860968022ca to fix confirm with jmobile backport commit 384e3812eb73a15adafb472cacfb93397a54459b to fix W3C/edit contract - + ***** ChangeLog for 3.3.5 compared to 3.3.4 ***** @@ -4361,7 +4450,7 @@ backport commit 384e3812eb73a15adafb472cacfb93397a54459b to fix W3C/edit contrac - Fix: [ bug #787 ] Invoice supplier box incorrect tooltip when delay on payment - Fix: [ bug #789 ] VAT not being calculated in POS - Fix: [ bug #790 ] Spanish localtax RE not being correctly calculated -- Fix: [ bug #794 ] Lost filter on zipcode in prospect list +- Fix: [ bug #794 ] Lost filter on zipcode in prospect list - Fix: [ bug #806 ] Margins module with orders2invoice does not respect cost price - Fix: [ bug #810 ] Cannot update ODT template path - Fix: [ bug #816 ] Sales journal does not reflect localtaxes @@ -4382,7 +4471,7 @@ backport commit 384e3812eb73a15adafb472cacfb93397a54459b to fix W3C/edit contrac ***** ChangeLog for 3.3.1 compared to 3.3 ***** - Fix: [ bug #733 ] Mass emailing tools do not support '; - print ' + print '
'; -
-
'; + print ''; print ''."\n"; print ''."\n"; @@ -1722,33 +1879,175 @@ if (preg_match('/^dopayment/', $action)) print ''; print ''; - print ' - - '; /* * Filter bar */ - $formTicket = new FormTicket($db); print ''; @@ -449,14 +458,6 @@ if ($action == "view_ticketlist") print ''; } - // Status - if (!empty($arrayfields['t.fk_statut']['checked'])) { - print ''; - } - if (!empty($arrayfields['t.subject']['checked'])) { print ''; + } + print ''; } - // ref + // Ref if (!empty($arrayfields['t.ref']['checked'])) { print ''; } - // Statut - if (!empty($arrayfields['t.fk_statut']['checked'])) { - print ''; - } - // Subject if (!empty($arrayfields['t.subject']['checked'])) { print ''; + } + print ''; + $i++; print ''; } @@ -670,7 +681,7 @@ if ($action == "view_ticketlist") print ''; print ''; print ''; - print ''; + //print ''; print '

'; print ''; @@ -688,6 +699,11 @@ if ($action == "view_ticketlist") print "\n"; } +print ""; + // End of page -llxFooter(); +htmlPrintOnlinePaymentFooter($mysoc, $langs, 1, $suffix, $object); + +llxFooter('', 'public'); + $db->close(); diff --git a/htdocs/public/ticket/view.php b/htdocs/public/ticket/view.php index af4585622dd..a0311ce1430 100644 --- a/htdocs/public/ticket/view.php +++ b/htdocs/public/ticket/view.php @@ -39,6 +39,9 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; // Load translation files required by the page $langs->loadLangs(array("companies","other","ticket")); @@ -118,7 +121,8 @@ if ($action == "view_ticket" || $action == "add_message" || $action == "close" | $action = ''; } } -$object->doActions($action); + +//$object->doActions($action); @@ -261,7 +265,7 @@ if ($action == "view_ticket" || $action == "add_message" || $action == "close" | print ''; print ''; print ''; - print ''; + //print ''; print "\n"; print '

'; @@ -317,6 +321,11 @@ if ($action == "view_ticket" || $action == "add_message" || $action == "close" | print "
\n"; } +print ""; + // End of page -llxFooter(); +htmlPrintOnlinePaymentFooter($mysoc, $langs, 1, $suffix, $object); + +llxFooter('', 'public'); + $db->close(); diff --git a/htdocs/reception/class/reception.class.php b/htdocs/reception/class/reception.class.php index 29bd65f6ae0..15c021f53bc 100644 --- a/htdocs/reception/class/reception.class.php +++ b/htdocs/reception/class/reception.class.php @@ -444,7 +444,7 @@ class Reception extends CommonObject $this->getUrlTrackingStatus($obj->tracking_number); /* - * Thirparty + * Thirdparty */ $result=$this->fetch_thirdparty(); @@ -707,8 +707,8 @@ class Reception extends CommonObject * @param int $qty Quantity * @param array $array_options extrafields array * @param string $comment Comment for stock movement - * @param date $eatby eat-by date - * @param date $sellby sell-by date + * @param integer $eatby eat-by date + * @param integer $sellby sell-by date * @param string $batch Lot number * @return int <0 if KO, >0 if OK */ @@ -1183,7 +1183,9 @@ class Reception extends CommonObject public function initAsSpecimen() { global $langs; - dol_include_once('/fourn/class/fournisseur.commande.dispatch.class.php'); + + include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php'; + include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php'; $now=dol_now(); dol_syslog(get_class($this)."::initAsSpecimen"); @@ -1207,7 +1209,7 @@ class Reception extends CommonObject } } - $order=new Commande($this->db); + $order=new CommandeFournisseur($this->db); $order->initAsSpecimen(); // Initialise parametres @@ -1256,7 +1258,7 @@ class Reception extends CommonObject * Set the planned delivery date * * @param User $user Objet utilisateur qui modifie - * @param timestamp $date_livraison Date de livraison + * @param integer $date_livraison Date de livraison * @return int <0 if KO, >0 if OK */ public function set_date_livraison($user, $date_livraison) diff --git a/htdocs/resource/element_resource.php b/htdocs/resource/element_resource.php index bba4d329c24..2e4085200a2 100644 --- a/htdocs/resource/element_resource.php +++ b/htdocs/resource/element_resource.php @@ -31,6 +31,9 @@ if (! empty($conf->projet->enabled)) { require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php'; } +if (! empty($conf->product->enabled) || ! empty($conf->service->enabled)) { + require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; +} // Load translation files required by the page $langs->loadLangs(array('resource', 'other', 'interventions')); @@ -79,7 +82,6 @@ if ($socid > 0) // Special for thirdparty if ($action == 'add_element_resource' && ! $cancel) { - $error++; $res = 0; if (! ($resource_id > 0)) { @@ -89,16 +91,20 @@ if ($action == 'add_element_resource' && ! $cancel) } else { - $objstat = fetchObjectByElement($element_id, $element); + $objstat = fetchObjectByElement($element_id, $element, $element_ref); $objstat->element = $element; // For externals module, we need to keep @xx $res = $objstat->add_element_resource($resource_id, $resource_type, $busy, $mandatory); } if (! $error && $res > 0) { setEventMessages($langs->trans('ResourceLinkedWithSuccess'), null, 'mesgs'); - header("Location: ".$_SERVER['PHP_SELF'].'?element='.$element.'&element_id='.$element_id); + header("Location: ".$_SERVER['PHP_SELF'].'?element='.$element.'&element_id='.$objstat->id); exit; } + elseif ($objstat) + { + setEventMessages($objstat->error, $objstat->errors, 'errors'); + } } // Update ressource @@ -416,6 +422,31 @@ else } } + // Specific to product/service module + if (($element_id || $element_ref) && ($element == 'product' || $element == 'service')) + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; + + $product = new Product($db); + $product->fetch($element_id, $element_ref); + + if (is_object($product)) + { + + $head = product_prepare_head($product); + $titre=$langs->trans("CardProduct".$product->type); + $picto=($product->type==Product::TYPE_SERVICE?'service':'product'); + + dol_fiche_head($head, 'resources', $titre, -1, $picto); + + $shownav = 1; + if ($user->societe_id && ! in_array('product', explode(',', $conf->global->MAIN_MODULES_FOR_EXTERNAL))) $shownav=0; + dol_banner_tab($product, 'ref', '', $shownav, 'ref', 'ref', '', '&element='.$element); + + dol_fiche_end(); + } + } + // hook for other elements linked $parameters=array('element'=>$element, 'element_id'=>$element_id, 'element_ref'=>$element_ref); diff --git a/htdocs/societe/canvas/actions_card_common.class.php b/htdocs/societe/canvas/actions_card_common.class.php index ca8d67d1125..9c19de507cf 100644 --- a/htdocs/societe/canvas/actions_card_common.class.php +++ b/htdocs/societe/canvas/actions_card_common.class.php @@ -58,7 +58,7 @@ abstract class ActionsCardCommon * Get object from id or ref and save it into this->object * * @param int $id Object id - * @param ref $ref Object ref + * @param string $ref Object ref * @return object Object loaded */ protected function getObject($id, $ref = '') diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index 69f76cacab8..292d109fd74 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -1370,12 +1370,12 @@ else print "\n"; print ''; print "\n"; $s.=''.$langs->trans("VATIntraCheck").''; - $s = $form->textwithpicto($s, $langs->trans("VATIntraCheckDesc", $langs->trans("VATIntraCheck")), 1); + $s = $form->textwithpicto($s, $langs->trans("VATIntraCheckDesc", $langs->transnoentitiesnoconv("VATIntraCheck")), 1); } else { @@ -1441,7 +1441,7 @@ else if (! empty($conf->global->MAIN_MULTILANGS)) { print ''; print ''; } diff --git a/htdocs/societe/class/api_thirdparties.class.php b/htdocs/societe/class/api_thirdparties.class.php index 954535d1c87..adae31e7677 100644 --- a/htdocs/societe/class/api_thirdparties.class.php +++ b/htdocs/societe/class/api_thirdparties.class.php @@ -1582,9 +1582,9 @@ $reshook = $hookmanager->executeHooks('replaceThirdparty', array( * @param int $id ID of thirdparty * * @return void - * @throws 401 Unauthorized: User does not have permission to delete thirdparties gateways - * @throws 404 Not Found: Specified thirdparty ID does not belongs to an existing thirdparty - * @throws 500 Internal Server Error: Error deleting SocieteAccount entity + * @throws RestException(401) Unauthorized: User does not have permission to delete thirdparties gateways + * @throws RestException(404) Not Found: Specified thirdparty ID does not belongs to an existing thirdparty + * @throws RestException(500) Internal Server Error: Error deleting SocieteAccount entity * * @url DELETE {id}/gateways */ diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index 9af346b35e3..4bec5fc6f49 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -60,7 +60,7 @@ class Societe extends CommonObject public $fieldsforcombobox='nom,name_alias'; protected $childtables=array("supplier_proposal"=>'SupplierProposal',"propal"=>'Proposal',"commande"=>'Order',"facture"=>'Invoice',"facture_rec"=>'RecurringInvoiceTemplate',"contrat"=>'Contract',"fichinter"=>'Fichinter',"facture_fourn"=>'SupplierInvoice',"commande_fournisseur"=>'SupplierOrder',"projet"=>'Project',"expedition"=>'Shipment',"prelevement_lignes"=>'DirectDebitRecord'); // To test if we can delete object - protected $childtablesoncascade=array("societe_prices", "societe_log", "societe_address", "product_fournisseur_price", "product_customer_price_log", "product_customer_price", "socpeople", "adherent", "societe_rib", "societe_remise", "societe_remise_except", "societe_commerciaux", "categorie", "notify", "notify_def", "actioncomm"); + protected $childtablesoncascade=array("societe_prices", "societe_log", "societe_address", "product_fournisseur_price", "product_customer_price_log", "product_customer_price", "socpeople", "adherent", "societe_account", "societe_rib", "societe_remise", "societe_remise_except", "societe_commerciaux", "categorie", "notify", "notify_def", "actioncomm"); public $picto = 'company'; /** @@ -1136,12 +1136,16 @@ class Societe extends CommonObject //$lmember->firstname=$this->firstname?$this->firstname:$lmember->firstname; // We keep firstname and lastname of member unchanged //$lmember->lastname=$this->lastname?$this->lastname:$lmember->lastname; // We keep firstname and lastname of member unchanged $lmember->address=$this->address; + $lmember->zip=$this->zip; + $lmember->town=$this->town; $lmember->email=$this->email; $lmember->skype=$this->skype; $lmember->twitter=$this->twitter; $lmember->facebook=$this->facebook; $lmember->linkedin=$this->linkedin; $lmember->phone=$this->phone; + $lmember->state_id=$this->state_id; + $lmember->country_id=$this->country_id; $result=$lmember->update($user, 0, 1, 1, 1); // Use nosync to 1 to avoid cyclic updates if ($result < 0) @@ -1252,7 +1256,7 @@ class Societe extends CommonObject $sql .= ', s.fk_forme_juridique as forme_juridique_code'; $sql .= ', s.webservices_url, s.webservices_key'; $sql .= ', s.code_client, s.code_fournisseur, s.code_compta, s.code_compta_fournisseur, s.parent, s.barcode'; - $sql .= ', s.fk_departement, s.fk_pays as country_id, s.fk_stcomm, s.remise_supplier, s.mode_reglement, s.cond_reglement, s.fk_account, s.tva_assuj'; + $sql .= ', s.fk_departement as state_id, s.fk_pays as country_id, s.fk_stcomm, s.remise_supplier, s.mode_reglement, s.cond_reglement, s.fk_account, s.tva_assuj'; $sql .= ', s.mode_reglement_supplier, s.cond_reglement_supplier, s.localtax1_assuj, s.localtax1_value, s.localtax2_assuj, s.localtax2_value, s.fk_prospectlevel, s.default_lang, s.logo'; $sql .= ', s.fk_shipping_method'; $sql .= ', s.outstanding_limit, s.import_key, s.canvas, s.fk_incoterms, s.location_incoterms'; @@ -1328,7 +1332,7 @@ class Societe extends CommonObject $this->country_code = $obj->country_id?$obj->country_code:''; $this->country = $obj->country_id?($langs->trans('Country'.$obj->country_code)!='Country'.$obj->country_code?$langs->transnoentities('Country'.$obj->country_code):$obj->country):''; - $this->state_id = $obj->fk_departement; + $this->state_id = $obj->state_id; $this->state_code = $obj->state_code; $this->state = ($obj->state!='-'?$obj->state:''); @@ -1457,7 +1461,7 @@ class Societe extends CommonObject * Delete a third party from database and all its dependencies (contacts, rib...) * * @param int $id Id of third party to delete - * @param User $fuser User who ask to delete thirparty + * @param User $fuser User who ask to delete thirdparty * @param int $call_trigger 0=No, 1=yes * @return int <0 if KO, 0 if nothing done, >0 if OK */ diff --git a/htdocs/societe/consumption.php b/htdocs/societe/consumption.php index 5d7bd6e70ad..c16ef378bd0 100644 --- a/htdocs/societe/consumption.php +++ b/htdocs/societe/consumption.php @@ -22,7 +22,7 @@ /** * \file htdocs/societe/consumption.php * \ingroup societe - * \brief Add a tab on thirpdarty view to list all products/services bought or sells by thirdparty + * \brief Add a tab on thirdparty view to list all products/services bought or sells by thirdparty */ require "../main.inc.php"; diff --git a/htdocs/societe/list.php b/htdocs/societe/list.php index d13c7c52254..c4a9ebf1b94 100644 --- a/htdocs/societe/list.php +++ b/htdocs/societe/list.php @@ -564,7 +564,7 @@ $arrayofmassactions = array( // 'builddoc'=>$langs->trans("PDFMerge"), ); //if($user->rights->societe->creer) $arrayofmassactions['createbills']=$langs->trans("CreateInvoiceForThisCustomer"); -if ($user->rights->societe->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete"); +if ($user->rights->societe->supprimer) $arrayofmassactions['predelete']=''.$langs->trans("Delete"); if (GETPOST('nomassaction', 'int') || in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); diff --git a/htdocs/societe/note.php b/htdocs/societe/note.php index 1cee0191f6c..4162808e967 100644 --- a/htdocs/societe/note.php +++ b/htdocs/societe/note.php @@ -87,7 +87,7 @@ if ($object->id > 0) print '
'; print '
'; - print '
+ if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION) || ! empty($paymentintent)) + { + print ' + +
'; -
- -
- -
- - -
-
- - -
+ if (! empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) + { + print '
'; + } - + print ' +
- + '; - '."\n"; + + // Code to ask the credit card. This use the default "API version". No way to force API version when using JS code. + print ''; } } diff --git a/htdocs/public/payment/paymentok.php b/htdocs/public/payment/paymentok.php index 62d709fc674..52413921f90 100644 --- a/htdocs/public/payment/paymentok.php +++ b/htdocs/public/payment/paymentok.php @@ -280,7 +280,7 @@ $fulltag = $FULLTAG; $tmptag=dolExplodeIntoArray($fulltag, '.', '='); -dol_syslog("ispaymentok=".$ispaymentok, LOG_DEBUG, 0, '_payment'); +dol_syslog("ispaymentok=".$ispaymentok." tmptag=".var_export($tmptag, true), LOG_DEBUG, 0, '_payment'); // Make complementary actions @@ -296,7 +296,7 @@ if ($ispaymentok) $user->rights->facture->creer = 1; $user->rights->adherent->cotisation->creer = 1; - if (in_array('MEM', array_keys($tmptag))) + if (array_key_exists('MEM', $tmptag) && $tmptag['MEM'] > 0) { // Validate member // Create subscription @@ -583,7 +583,7 @@ if ($ispaymentok) $ispostactionok = -1; } } - elseif (in_array('INV', array_keys($tmptag))) + elseif (array_key_exists('INV', $tmptag) && $tmptag['INV'] > 0) { // Record payment include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; @@ -769,14 +769,14 @@ if ($ispaymentok) $urlback=$_SERVER["REQUEST_URI"]; $topic='['.$appli.'] '.$companylangs->transnoentitiesnoconv("NewOnlinePaymentReceived"); $content=""; - if (in_array('MEM', array_keys($tmptag))) + if (array_key_exists('MEM', $tmptag)) { $url=$urlwithroot."/adherents/subscription.php?rowid=".$tmptag['MEM']; $content.=''.$companylangs->trans("PaymentSubscription")."

\n"; $content.=$companylangs->trans("MemberId").': '.$tmptag['MEM']."
\n"; $content.=$companylangs->trans("Link").': '.$url.''."
\n"; } - elseif (in_array('INV', array_keys($tmptag))) + elseif (array_key_exists('INV', $tmptag)) { $url=$urlwithroot."/compta/facture/card.php?id=".$tmptag['INV']; $content.=''.$companylangs->trans("Payment")."

\n"; diff --git a/htdocs/public/stripe/confirm_payment.php b/htdocs/public/stripe/confirm_payment.php new file mode 100644 index 00000000000..ea59b13dcb9 --- /dev/null +++ b/htdocs/public/stripe/confirm_payment.php @@ -0,0 +1,159 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +define("NOLOGIN", 1); // This means this output page does not require to be logged. +define("NOCSRFCHECK", 1); // We accept to go on this page from external web site. + +$entity=(! empty($_GET['entity']) ? (int) $_GET['entity'] : (! empty($_POST['entity']) ? (int) $_POST['entity'] : 1)); +if (is_numeric($entity)) define("DOLENTITY", $entity); + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/ccountry.class.php'; +require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; + +require_once DOL_DOCUMENT_ROOT.'/includes/stripe/init.php'; +require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php'; + +if (empty($conf->stripe->enabled)) accessforbidden('', 0, 0, 1); + + +// You can find your endpoint's secret in your webhook settings +if (isset($_GET['connect'])) +{ + if (isset($_GET['test'])) + { + $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_CONNECT_KEY; + $service = 'StripeTest'; + $servicestatus = 0; + } + else + { + $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_CONNECT_KEY; + $service = 'StripeLive'; + $servicestatus = 1; + } +} +else { + if (isset($_GET['test'])) + { + $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_KEY; + $service = 'StripeTest'; + $servicestatus = 0; + } + else + { + $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_KEY; + $service = 'StripeLive'; + $servicestatus = 1; + } +} + + + +/* + * Actions + */ + +$langs->load("main"); + +// TODO Do we really need a user in setup just to have an name to fill an email topic when it is a technical system notification email +$user = new User($db); +$user->fetch($conf->global->STRIPE_USER_ACCOUNT_FOR_ACTIONS); +$user->getrights(); + +// list of action +$stripe=new Stripe($db); + +// Subject +$societeName = $conf->global->MAIN_INFO_SOCIETE_NOM; +if (! empty($conf->global->MAIN_APPLICATION_TITLE)) $societeName = $conf->global->MAIN_APPLICATION_TITLE; + + +dol_syslog("Stripe confirm_payment was called"); +dol_syslog("GET=".var_export($_GET, true)); +dol_syslog("POST=".var_export($_POST, true)); + + +header('Content-Type: application/json'); + +// retrieve json from POST body +$json_str = file_get_contents('php://input'); +$json_obj = json_decode($json_str); + +$intent = null; +try { + if (isset($json_obj->payment_method_id)) { + // Create the PaymentIntent + $intent = \Stripe\PaymentIntent::create([ + 'payment_method' => $json_obj->payment_method_id, + 'amount' => 1099, + 'currency' => 'eur', + 'confirmation_method' => 'manual', + 'confirm' => true, + ]); + } + if (isset($json_obj->payment_intent_id)) { + $intent = \Stripe\PaymentIntent::retrieve( + $json_obj->payment_intent_id + ); + $intent->confirm(); + } + generatePaymentResponse($intent); +} catch (\Stripe\Error\Base $e) { + // Display error on client + echo json_encode([ + 'error' => $e->getMessage() + ]); +} + +/* + * generate payment response + * + * @param \Stripe\PaymentIntent $intent PaymentIntent + * @return void + */ +function generatePaymentResponse($intent) +{ + if ($intent->status == 'requires_source_action' && + $intent->next_action->type == 'use_stripe_sdk') { + // Tell the client to handle the action + echo json_encode([ + 'requires_action' => true, + 'payment_intent_client_secret' => $intent->client_secret + ]); + } elseif ($intent->status == 'succeeded') { + // The payment didn’t need any additional actions and completed! + // Handle post-payment fulfillment + + // TODO + + echo json_encode([ + "success" => true + ]); + } else { + // Invalid status + http_response_code(500); + echo json_encode(['error' => 'Invalid PaymentIntent status']); + } +} diff --git a/htdocs/public/stripe/ipn.php b/htdocs/public/stripe/ipn.php index 542dc113f43..ca7db10f9d1 100644 --- a/htdocs/public/stripe/ipn.php +++ b/htdocs/public/stripe/ipn.php @@ -25,18 +25,21 @@ if (is_numeric($entity)) define("DOLENTITY", $entity); require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; -require_once DOL_DOCUMENT_ROOT.'/includes/stripe/init.php'; -require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/ccountry.class.php'; require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; -require_once DOL_DOCUMENT_ROOT .'/core/class/CMailFile.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; + +require_once DOL_DOCUMENT_ROOT.'/includes/stripe/init.php'; +require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php'; + if (empty($conf->stripe->enabled)) accessforbidden('', 0, 0, 1); + // You can find your endpoint's secret in your webhook settings if (isset($_GET['connect'])) { @@ -50,7 +53,7 @@ if (isset($_GET['connect'])) { $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_CONNECT_KEY; $service = 'StripeLive'; - $servicestatus = 1; + $servicestatus = 1; } } else { @@ -68,6 +71,18 @@ else { } } +if (empty($endpoint_secret)) +{ + print 'Error: Setup of module Stripe not complete for mode '.$service.'. The WEBHOOK_KEY is not defined.'; + http_response_code(400); // PHP 5.4 or greater + exit(); +} + + +/* + * Actions + */ + $payload = @file_get_contents("php://input"); $sig_header = $_SERVER["HTTP_STRIPE_SIGNATURE"]; $event = null; @@ -89,8 +104,6 @@ catch(\UnexpectedValueException $e) { // Do something with $event -http_response_code(200); // PHP 5.4 or greater - $langs->load("main"); // TODO Do we really need a user in setup just to have an name to fill an email topic when it is a technical system notification email @@ -98,7 +111,8 @@ $user = new User($db); $user->fetch($conf->global->STRIPE_USER_ACCOUNT_FOR_ACTIONS); $user->getrights(); -if (! empty($conf->multicompany->enabled) && ! empty($conf->stripeconnect->enabled) && is_object($mc)) { +if (! empty($conf->multicompany->enabled) && ! empty($conf->stripeconnect->enabled) && is_object($mc)) +{ $sql = "SELECT entity"; $sql.= " FROM ".MAIN_DB_PREFIX."oauth_token"; $sql.= " WHERE service = '".$db->escape($service)."' and tokenstring = '%".$db->escape($event->account)."%'"; @@ -126,6 +140,15 @@ if (! empty($conf->multicompany->enabled) && ! empty($conf->stripeconnect->enabl // list of action $stripe=new Stripe($db); + +// Subject +$societeName = $conf->global->MAIN_INFO_SOCIETE_NOM; +if (! empty($conf->global->MAIN_APPLICATION_TITLE)) $societeName = $conf->global->MAIN_APPLICATION_TITLE; + + +dol_syslog("Stripe IPN was called with event->type = ".$event->type); + + if ($event->type == 'payout.created') { $error=0; @@ -133,7 +156,8 @@ if ($event->type == 'payout.created') { if ($result > 0) { - $subject = '[NOTIFICATION] Stripe payout scheduled'; + + $subject = $societeName.' - [NOTIFICATION] Stripe payout scheduled'; if (!empty($user->email)) { $sendto = dolGetFirstLastname($user->firstname, $user->lastname) . " <".$user->email.">"; } else { @@ -163,11 +187,13 @@ if ($event->type == 'payout.created') { $ret = $mailfile->sendfile(); + http_response_code(200); // PHP 5.4 or greater return 1; } else { $error++; + http_response_code(500); // PHP 5.4 or greater return -1; } } @@ -212,7 +238,7 @@ elseif ($event->type == 'payout.paid') { if (! ($result > 0)) $error++; } - $subject = '[NOTIFICATION] Stripe payout done'; + $subject = $societeName.' - [NOTIFICATION] Stripe payout done'; if (!empty($user->email)) { $sendto = dolGetFirstLastname($user->firstname, $user->lastname) . " <".$user->email.">"; } else { @@ -226,7 +252,7 @@ elseif ($event->type == 'payout.paid') { $message = "A bank transfer of ".price2num($event->data->object->amount/100)." ".$event->data->object->currency." has been done to your account the ".dol_print_date($event->data->object->arrival_date, 'dayhour'); -$mailfile = new CMailFile( + $mailfile = new CMailFile( $subject, $sendto, $replyto, @@ -242,18 +268,16 @@ $mailfile = new CMailFile( $ret = $mailfile->sendfile(); + http_response_code(200); // PHP 5.4 or greater return 1; } else { $error++; + http_response_code(500); // PHP 5.4 or greater return -1; } } -elseif ($event->type == 'charge.succeeded') { - - //TODO: create fees -} elseif ($event->type == 'customer.source.created') { //TODO: save customer's source @@ -266,130 +290,39 @@ elseif ($event->type == 'customer.source.delete') { //TODO: delete customer's source } +elseif ($event->type == 'customer.deleted') { + $db->begin(); + $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_account WHERE key_account = '".$db->escape($event->data->object->id)."' and site='stripe'"; + dol_syslog(get_class($this) . "::delete sql=" . $sql, LOG_DEBUG); + $db->query($sql); + $db->commit(); +} +elseif ($event->type == 'payment_intent.succeeded') { + // TODO: Redirect to paymentok.php +} +elseif ($event->type == 'payment_intent.payment_failed') { + // TODO: Redirect to paymentko.php +} +elseif ($event->type == 'charge.succeeded') { + // TODO: create fees + // TODO: Redirect to paymentok.php +} elseif ($event->type == 'charge.failed') { - - $subject = 'Your payment has been received: '.$event->data->object->id.''; - $headers = 'From: "'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; + // TODO: Redirect to paymentko.php } elseif (($event->type == 'source.chargeable') && ($event->data->object->type == 'three_d_secure') && ($event->data->object->three_d_secure->authenticated==true)) { $fulltag=$event->data->object->metadata->FULLTAG; - // Save into $tmptag all metadata + dol_syslog("fulltag=".$fulltag); + // Save into $tmptag all metadata $tmptag=dolExplodeIntoArray($fulltag, '.', '='); - if (! empty($tmptag['ORD'])) { - $order=new Commande($db); - $order->fetch('', $tmptag['ORD']); - $origin='order'; - $item=$order->id; - } elseif (! empty($tmptag['INV'])) { - $invoice = new Facture($db); - $invoice->fetch('', $tmptag['INV']); - $origin='invoice'; - $item=$invoice->id; - } - $stripe=new Stripe($db); + /* $stripeacc = $stripe->getStripeAccount($service); // Stripe OAuth connect account of dolibarr user (no network access here) $stripecu = $stripe->getStripeCustomerAccount($tmptag['CUS'], $servicestatus); // Get thirdparty cu_... $charge=$stripe->createPaymentStripe($event->data->object->amount/100, $event->data->object->currency, $origin, $item, $event->data->object->id, $stripecu, $stripeacc, $servicestatus); - - if (isset($charge->id) && $charge->statut=='error') { - $msg=$charge->message; - $code=$charge->code; - $error++; - } - elseif (isset($charge->id) && $charge->statut=='success' && (! empty($tmptag['ORD']))) { - //$order=new Commande($db); - //$order->fetch('',$tmptag['ORD']); - $invoice = new Facture($db); - $idinv=$invoice->createFromOrder($order, $user); - - if ($idinv > 0) - { - $result=$invoice->validate($user); - if ($result > 0) { - $invoice->fetch($idinv); - $paiement = $invoice->getSommePaiement(); - $creditnotes=$invoice->getSumCreditNotesUsed(); - $deposits=$invoice->getSumDepositsUsed(); - $ref=$invoice->ref; - $ifverif=$invoice->socid; - $currency=$invoice->multicurrency_code; - $total=price2num($invoice->total_ttc - $paiement - $creditnotes - $deposits, 'MT'); - } else { - $msg=$invoice->error; - $error++; - } - } else { - $msg=$invoice->error; - $error++; - } - } - - if (!$error) { - $datepaye = dol_now(); - $paymentType ="CB"; - $amounts=array(); - $amounts[$invoice->id] = $total; - $multicurrency_amounts=array(); - //$multicurrency_amounts[$item] = $total; - $paiement = new Paiement($db); - $paiement->datepaye = $datepaye; - $paiement->amounts = $amounts; // Array with all payments dispatching - $paiement->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching - $paiement->paiementid = dol_getIdFromCode($db, $paymentType, 'c_paiement', 'code', 'id', 1); - $paiement->num_paiement = $charge->message; - $paiement->note = ''; - } - - if (! $error) { - $paiement_id=$paiement->create($user, 0); - - if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE) && count($invoice->lines)) { - $outputlangs = $langs; - $newlang = ''; - if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09'); - if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $invoice->thirdparty->default_lang; - if (! empty($newlang)) { - $outputlangs = new Translate("", $conf); - $outputlangs->setDefaultLang($newlang); - } - $model=$invoice->modelpdf; - $ret = $invoice->fetch($invoice->id); // Reload to get new records - - $invoice->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref); - } - if ($paiement_id < 0) { - $msg=$paiement->errors; - $error++; - } else { - if ($event->data->object->metadata->source=='order') { - $order->classifyBilled($user); - } - } - } - - if (! $error) { - $label='(CustomerInvoicePayment)'; - if (GETPOST('type') == 2) $label='(CustomerInvoicePaymentBack)'; - $paiement->addPaymentToBank($user, 'payment', $label, $conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS, '', ''); - if ($result < 0) - { - $msg=$paiement->errors; - $error++; - } - $invoice->set_paid($user); - } - - $body = ""; - $subject = 'Facture '.$invoice->ref; - $headers = 'From: "'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; -} -elseif ($event->type == 'customer.deleted') { - $db->begin(); - $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_account WHERE key_account = '".$event->data->object->id."' and site='stripe' "; - dol_syslog(get_class($this) . "::delete sql=" . $sql, LOG_DEBUG); - $db->query($sql); - $db->commit(); + */ } + +http_response_code(200); // PHP 5.4 or greater diff --git a/htdocs/public/ticket/create_ticket.php b/htdocs/public/ticket/create_ticket.php index 206dc65acd7..42774772eb5 100644 --- a/htdocs/public/ticket/create_ticket.php +++ b/htdocs/public/ticket/create_ticket.php @@ -33,6 +33,9 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; @@ -366,7 +369,8 @@ if ($action != "infos_success") { print '
'; // End of page +htmlPrintOnlinePaymentFooter($mysoc, $langs, 1, $suffix, $object); -llxFooter(''); +llxFooter('', 'public'); $db->close(); diff --git a/htdocs/public/ticket/index.php b/htdocs/public/ticket/index.php index 5e241c4edfb..05b992b3e40 100644 --- a/htdocs/public/ticket/index.php +++ b/htdocs/public/ticket/index.php @@ -36,6 +36,7 @@ require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; // Load translation files required by the page diff --git a/htdocs/public/ticket/list.php b/htdocs/public/ticket/list.php index 73b691500fd..443af658cb5 100644 --- a/htdocs/public/ticket/list.php +++ b/htdocs/public/ticket/list.php @@ -38,6 +38,9 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; // Load translation files required by the page $langs->loadLangs(array("companies","other","ticket")); @@ -58,7 +61,7 @@ if (isset($_SESSION['email_customer'])) { $email = $_SESSION['email_customer']; } -$object = new ActionsTicket($db); +$object = new Ticket($db); @@ -83,7 +86,7 @@ if ($action == "view_ticketlist") { } else { if (!isValidEmail($email)) { $error++; - array_push($object->errors, $langs->trans("ErrorEmailInvalid")); + array_push($object->errors, $langs->trans("ErrorEmailOrTrackingInvalid")); $action = ''; } } @@ -91,9 +94,9 @@ if ($action == "view_ticketlist") { if (!$error) { $ret = $object->fetch('', '', $track_id); - if ($ret && $object->dao->id > 0) { + if ($ret && $object->id > 0) { // vérifie si l'adresse email est bien dans les contacts du ticket - $contacts = $object->dao->liste_contact(-1, 'external'); + $contacts = $object->liste_contact(-1, 'external'); foreach ($contacts as $contact) { if ($contact['email'] == $email) { $display_ticket_list = true; @@ -104,12 +107,24 @@ if ($action == "view_ticketlist") { $display_ticket_list = false; } } - - if ($object->dao->fk_soc > 0) { - $object->dao->fetch_thirdparty(); + if ($object->fk_soc > 0) { + $object->fetch_thirdparty(); + if ($email == $object->thirdparty->email) { + $display_ticket_list = true; + $_SESSION['email_customer'] = $email; + $_SESSION['track_id_customer'] = $track_id; + } } - - if ($email == $object->dao->origin_email || $email == $object->dao->thirdparty->email) { + if ($object->fk_user_create > 0) { + $tmpuser=new User($db); + $tmpuser->fetch($object->fk_user_create); + if ($email == $tmpuser->email) { + $display_ticket_list = true; + $_SESSION['email_customer'] = $email; + $_SESSION['track_id_customer'] = $track_id; + } + } + if ($email == $object->origin_email) { $display_ticket_list = true; $_SESSION['email_customer'] = $email; $_SESSION['track_id_customer'] = $track_id; @@ -127,7 +142,7 @@ if ($action == "view_ticketlist") { } } -$object->doActions($action); +//$object->doActions($action); @@ -138,10 +153,11 @@ $object->doActions($action); $form = new Form($db); $user_assign = new User($db); $user_create = new User($db); -$formticket = new FormTicket($db); +$formTicket = new FormTicket($db); $arrayofjs = array(); $arrayofcss = array('/ticket/css/styles.css.php'); + llxHeaderTicket($langs->trans("Tickets"), "", 0, 0, $arrayofjs, $arrayofcss); if (!$conf->global->TICKET_ENABLE_PUBLIC_INTERFACE) { @@ -154,7 +170,6 @@ print '
'; if ($action == "view_ticketlist") { - if ($display_ticket_list) { // Filters $search_fk_status = GETPOST("search_fk_status", 'alpha'); @@ -185,7 +200,7 @@ if ($action == "view_ticketlist") $search_array_options = $extrafields->getOptionalsFromPost('ticket', '', 'search_'); $filter = array(); - $param = ''; + $param = 'action=view_ticketlist'; // Definition of fields for list $arrayfields = array( @@ -247,12 +262,10 @@ if ($action == "view_ticketlist") $param .= '&search_fk_user_create=' . $search_fk_user_create; } } - if ((isset($search_fk_status) && $search_fk_status != '') && $search_fk_status != '-1' && $search_fk_status != 'non_closed') { $filter['t.fk_statut'] = $search_fk_status; $param .= '&search_fk_status=' . $search_fk_status; } - if (isset($search_fk_status) && $search_fk_status == 'non_closed') { $filter['t.fk_statut'] = array(0, 1, 3, 4, 5, 6); $param .= '&search_fk_status=non_closed'; @@ -266,7 +279,6 @@ if ($action == "view_ticketlist") if (!$sortfield) { $sortfield = 't.datec'; } - if (!$sortorder) { $sortorder = 'DESC'; } @@ -361,9 +373,7 @@ if ($action == "view_ticketlist") $num = $db->num_rows($resql); print_barre_liste($langs->trans('TicketList'), $page, 'public/list.php', $param, $sortfield, $sortorder, '', $num, $num_total, 'ticket'); - /* - * Search bar - */ + // Search bar print '
' . "\n"; print ''; print ''; @@ -388,9 +398,6 @@ if ($action == "view_ticketlist") if (!empty($arrayfields['t.ref']['checked'])) { print_liste_field_titre($arrayfields['t.ref']['label'], $url_page_current, 't.ref', '', $param, '', $sortfield, $sortorder); } - if (!empty($arrayfields['t.fk_statut']['checked'])) { - print_liste_field_titre($arrayfields['t.fk_statut']['label'], $url_page_current, 't.fk_statut', '', $param, '', $sortfield, $sortorder); - } if (!empty($arrayfields['t.subject']['checked'])) { print_liste_field_titre($arrayfields['t.subject']['label']); } @@ -424,13 +431,15 @@ if ($action == "view_ticketlist") } } } + if (!empty($arrayfields['t.fk_statut']['checked'])) { + print_liste_field_titre($arrayfields['t.fk_statut']['label'], $url_page_current, 't.fk_statut', '', $param, '', $sortfield, $sortorder); + } print_liste_field_titre($selectedfields, $url_page_current, "", '', '', 'align="right"', $sortfield, $sortorder, 'maxwidthsearch '); print '
'; - $selected = ($search_fk_status != "non_closed" ? $search_fk_status : ''); - //$object->printSelectStatus($selected); - print ''; print ''; @@ -506,6 +507,14 @@ if ($action == "view_ticketlist") } } + // Status + if (!empty($arrayfields['t.fk_statut']['checked'])) { + print ''; + $selected = ($search_fk_status != "non_closed" ? $search_fk_status : ''); + //$object->printSelectStatus($selected); + print ''; print ''; print ''; @@ -537,21 +546,13 @@ if ($action == "view_ticketlist") print ''; print $obj->ref; print ''; - $object->fk_statut = $obj->fk_statut; - print $object->getLibStatut(2); - print ''; @@ -635,7 +636,17 @@ if ($action == "view_ticketlist") } } } + + // Statut + if (!empty($arrayfields['t.fk_statut']['checked'])) { + print ''; + $object->fk_statut = $obj->fk_statut; + print $object->getLibStatut(2); + print '
'.$form->editfieldkey('DefaultLang', 'default_lang', '', $object, 0).''."\n"; - print $formadmin->select_language(($object->default_lang?$object->default_lang:$conf->global->MAIN_LANG_DEFAULT), 'default_lang', 0, 0, 1, 0, 0, 'maxwidth200onsmartphone'); + print $formadmin->select_language(GETPOST('default_lang', 'alpha')?GETPOST('default_lang', 'alpha'):($object->default_lang?$object->default_lang:''), 'default_lang', 0, 0, 1, 0, 0, 'maxwidth200onsmartphone'); print '
'; + print '
'; if (! empty($conf->global->SOCIETE_USEPREFIX)) // Old not used prefix field { diff --git a/htdocs/societe/paymentmodes.php b/htdocs/societe/paymentmodes.php index f928658db3e..24bdedc946f 100644 --- a/htdocs/societe/paymentmodes.php +++ b/htdocs/societe/paymentmodes.php @@ -750,7 +750,6 @@ if ($socid && $action != 'edit' && $action != 'create' && $action != 'editcard' if ($conf->commande->enabled && $user->rights->commande->lire) $elementTypeArray['order']=$langs->transnoentitiesnoconv('Orders'); if ($conf->facture->enabled && $user->rights->facture->lire) $elementTypeArray['invoice']=$langs->transnoentitiesnoconv('Invoices'); if ($conf->contrat->enabled && $user->rights->contrat->lire) $elementTypeArray['contract']=$langs->transnoentitiesnoconv('Contracts'); - } if (! empty($conf->stripe->enabled)) { @@ -786,6 +785,7 @@ if ($socid && $action != 'edit' && $action != 'create' && $action != 'editcard' } print ''; } + } print '
'; print '
'; diff --git a/htdocs/societe/website.php b/htdocs/societe/website.php index 75ca2b0fcd5..0a5c62b9afb 100644 --- a/htdocs/societe/website.php +++ b/htdocs/societe/website.php @@ -317,7 +317,7 @@ $arrayofmassactions = array( //'presend'=>$langs->trans("SendByMail"), //'builddoc'=>$langs->trans("PDFMerge"), ); -if ($user->rights->mymodule->delete) $arrayofmassactions['predelete']=$langs->trans("Delete"); +if ($user->rights->mymodule->delete) $arrayofmassactions['predelete']=''.$langs->trans("Delete"); if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); diff --git a/htdocs/stripe/admin/stripe.php b/htdocs/stripe/admin/stripe.php index 5e2c27dd392..9b9914d6c7e 100644 --- a/htdocs/stripe/admin/stripe.php +++ b/htdocs/stripe/admin/stripe.php @@ -2,7 +2,7 @@ /* Copyright (C) 2017 Alexandre Spangaro * Copyright (C) 2017 Olivier Geffroy * Copyright (C) 2017 Saasprov - * Copyright (C) 2018 ptibogxiv + * Copyright (C) 2018-2019 Thibault FOUCART * Copyright (C) 2018 Frédéric France * * This program is free software; you can redistribute it and/or modify @@ -30,6 +30,7 @@ require_once DOL_DOCUMENT_ROOT.'/stripe/lib/stripe.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; +require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php'; $servicename='Stripe'; @@ -41,6 +42,10 @@ if (! $user->admin) accessforbidden(); $action = GETPOST('action', 'alpha'); +/* + * Actions + */ + if ($action == 'setvalue' && $user->admin) { $db->begin(); @@ -150,6 +155,8 @@ print ''; dol_fiche_head($head, 'stripeaccount', '', -1); +$stripearrayofwebhookevents=array('payout.created','payout.paid','charge.pending','charge.refunded','charge.succeeded','charge.failed','payment_intent.succeeded','payment_intent.payment_failed','source.chargeable','customer.deleted'); + print $langs->trans("StripeDesc")."
\n"; print '
'; @@ -158,6 +165,7 @@ print ''; print ''; print ''; print ''; +print ''; print "\n"; print ''; @@ -169,7 +177,7 @@ print $langs->trans("StripeLiveEnabled").''; +print ''; if (empty($conf->stripeconnect->enabled)) { @@ -177,24 +185,62 @@ if (empty($conf->stripeconnect->enabled)) print ''.$langs->trans("STRIPE_TEST_PUBLISHABLE_KEY").''; + print ''; print ''; + print ''; print ''; + print ''; } else { print ''; print ''; - print ''; + print ''; } if (empty($conf->stripeconnect->enabled)) @@ -212,29 +258,67 @@ if (empty($conf->stripeconnect->enabled)) print ''.$langs->trans("STRIPE_LIVE_PUBLISHABLE_KEY").''; + print ''; print ''; + print ''; print ''; + print ''; } else { print ''; - print ''; + print ''; } @@ -373,7 +457,7 @@ $token=''; include DOL_DOCUMENT_ROOT.'/core/tpl/onlinepaymentlinks.tpl.php'; -print info_admin($langs->trans("ExampleOfTestCreditCard", '4242424242424242', '4000000000000101', '4000000000000069', '4000000000000341')); +print info_admin($langs->trans("ExampleOfTestCreditCard", '4242424242424242 (no 3DSecure) or 4000000000003063 (3DSecure required) or 4000000000003220 (3DSecure2 required)', '4000000000000101', '4000000000000069', '4000000000000341')); if (! empty($conf->use_javascript_ajax)) { diff --git a/htdocs/stripe/charge.php b/htdocs/stripe/charge.php index fb122948dd0..e8823119e30 100644 --- a/htdocs/stripe/charge.php +++ b/htdocs/stripe/charge.php @@ -150,27 +150,30 @@ if (!$rowid) print ''; - if (!empty($stripeacc)) $connect=$stripeacc.'/'; + if (!empty($stripeacc)) $connect=$stripeacc.'/'; // Ref $url='https://dashboard.stripe.com/'.$connect.'test/payments/'.$charge->id; - if ($servicestatus) - { - $url='https://dashboard.stripe.com/'.$connect.'payments/'.$charge->id; - } - print "\n"; + if ($servicestatus) + { + $url='https://dashboard.stripe.com/'.$connect.'payments/'.$charge->id; + } + print "\n"; // Stripe customer print "\n"; + if (! empty($charge->customer)) + { + print ''.img_picto($langs->trans('ShowInStripe'), 'object_globe').' '.$charge->customer.''; + } + print "\n"; // Link print "'; } print '' . "\n"; @@ -1877,6 +1877,7 @@ if ($action == 'create') $modelmail='supplier_proposal_send'; $defaulttopic='SendAskRef'; $diroutput = $conf->supplier_proposal->dir_output; + $autocopy='MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO'; $trackid = 'spr'.$object->id; include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php'; diff --git a/htdocs/supplier_proposal/class/supplier_proposal.class.php b/htdocs/supplier_proposal/class/supplier_proposal.class.php index a767dceb450..ca16348dcd6 100644 --- a/htdocs/supplier_proposal/class/supplier_proposal.class.php +++ b/htdocs/supplier_proposal/class/supplier_proposal.class.php @@ -89,7 +89,7 @@ class SupplierProposal extends CommonObject /** * @deprecated - * @see user_author_id + * @see $user_author_id */ public $author; @@ -101,7 +101,7 @@ class SupplierProposal extends CommonObject /** * @deprecated - * @see date_creation + * @see $date_creation */ public $datec; @@ -113,7 +113,7 @@ class SupplierProposal extends CommonObject /** * @deprecated - * @see date_validation + * @see $date_validation */ public $datev; @@ -130,19 +130,19 @@ class SupplierProposal extends CommonObject /** * @deprecated - * @see price_ht + * @see $price_ht */ public $price; /** * @deprecated - * @see total_tva + * @see $total_tva */ public $tva; /** * @deprecated - * @see total_ttc + * @see $total_ttc */ public $total; @@ -393,7 +393,7 @@ class SupplierProposal extends CommonObject * @param double $pu_ht_devise Amount in currency * @return int >0 if OK, <0 if KO * - * @see add_product + * @see add_product() */ public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $price_base_type = 'HT', $pu_ttc = 0, $info_bits = 0, $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $array_option = 0, $ref_supplier = '', $fk_unit = '', $origin = '', $origin_id = 0, $pu_ht_devise = 0) { @@ -1104,7 +1104,7 @@ class SupplierProposal extends CommonObject * * @param User $user User that create * @return int Id of the new object if ok, <0 if ko - * @see create + * @see create() */ public function create_from($user) { @@ -1117,12 +1117,13 @@ class SupplierProposal extends CommonObject /** * Load an object from its id and create a new one in database * - * @param int $socid Id of thirdparty - * @return int New id of clone + * @param User $user User making the clone + * @param int $fromid Id of thirdparty + * @return int New id of clone */ - public function createFromClone($socid = 0) + public function createFromClone(User $user, $fromid = 0) { - global $user,$langs,$conf,$hookmanager; + global $conf,$hookmanager; $error=0; $now=dol_now(); @@ -1139,9 +1140,9 @@ class SupplierProposal extends CommonObject $objsoc=new Societe($this->db); // Change socid if needed - if (! empty($socid) && $socid != $this->socid) + if (! empty($fromid) && $fromid != $this->socid) { - if ($objsoc->fetch($socid) > 0) + if ($objsoc->fetch($fromid) > 0) { $this->socid = $objsoc->id; $this->cond_reglement_id = (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0); @@ -2760,7 +2761,7 @@ class SupplierProposalLine extends CommonObjectLine /** * @deprecated - * @see product_type + * @see $product_type */ public $fk_product_type; /** @@ -2831,7 +2832,7 @@ class SupplierProposalLine extends CommonObjectLine /** * @deprecated - * @see product_label + * @see $product_label */ public $libelle; diff --git a/htdocs/supplier_proposal/list.php b/htdocs/supplier_proposal/list.php index 249620b5f53..63c2a004982 100644 --- a/htdocs/supplier_proposal/list.php +++ b/htdocs/supplier_proposal/list.php @@ -381,7 +381,7 @@ if ($resql) //'presend'=>$langs->trans("SendByMail"), 'builddoc'=>$langs->trans("PDFMerge"), ); - if ($user->rights->supplier_proposal->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete"); + if ($user->rights->supplier_proposal->supprimer) $arrayofmassactions['predelete']=''.$langs->trans("Delete"); if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); diff --git a/htdocs/takepos/admin/setup.php b/htdocs/takepos/admin/setup.php index e17ea0ee036..b685d9e97d2 100644 --- a/htdocs/takepos/admin/setup.php +++ b/htdocs/takepos/admin/setup.php @@ -27,6 +27,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; +require_once DOL_DOCUMENT_ROOT."/core/lib/takepos.lib.php"; // If socid provided by ajax company selector if (! empty($_REQUEST['CASHDESK_ID_THIRDPARTY_id'])) @@ -63,6 +64,7 @@ if (GETPOST('action', 'alpha') == 'set') $db->begin(); if (GETPOST('socid', 'int') < 0) $_POST["socid"]=''; + /* $res = dolibarr_set_const($db, "CASHDESK_ID_THIRDPARTY", (GETPOST('socid', 'int') > 0 ? GETPOST('socid', 'int') : ''), 'chaine', 0, '', $conf->entity); $res = dolibarr_set_const($db, "CASHDESK_ID_BANKACCOUNT_CASH", (GETPOST('CASHDESK_ID_BANKACCOUNT_CASH', 'alpha') > 0 ? GETPOST('CASHDESK_ID_BANKACCOUNT_CASH', 'alpha') : ''), 'chaine', 0, '', $conf->entity); @@ -75,6 +77,8 @@ if (GETPOST('action', 'alpha') == 'set') } $res = dolibarr_set_const($db, "CASHDESK_ID_WAREHOUSE", (GETPOST('CASHDESK_ID_WAREHOUSE', 'alpha') > 0 ? GETPOST('CASHDESK_ID_WAREHOUSE', 'alpha') : ''), 'chaine', 0, '', $conf->entity); $res = dolibarr_set_const($db, "CASHDESK_NO_DECREASE_STOCK", GETPOST('CASHDESK_NO_DECREASE_STOCK', 'alpha'), 'chaine', 0, '', $conf->entity); + */ + $res = dolibarr_set_const($db, "CASHDESK_SERVICES", GETPOST('CASHDESK_SERVICES', 'alpha'), 'chaine', 0, '', $conf->entity); $res = dolibarr_set_const($db, "TAKEPOS_ROOT_CATEGORY_ID", GETPOST('TAKEPOS_ROOT_CATEGORY_ID', 'alpha'), 'chaine', 0, '', $conf->entity); @@ -88,6 +92,7 @@ if (GETPOST('action', 'alpha') == 'set') $res = dolibarr_set_const($db, "TAKEPOS_HEADER", GETPOST('TAKEPOS_HEADER', 'alpha'), 'chaine', 0, '', $conf->entity); $res = dolibarr_set_const($db, "TAKEPOS_FOOTER", GETPOST('TAKEPOS_FOOTER', 'alpha'), 'chaine', 0, '', $conf->entity); $res = dolibarr_set_const($db, "TAKEPOS_NUMPAD", GETPOST('TAKEPOS_NUMPAD', 'alpha'), 'chaine', 0, '', $conf->entity); + $res = dolibarr_set_const($db, "TAKEPOS_NUM_TERMINALS", GETPOST('TAKEPOS_NUM_TERMINALS', 'alpha'), 'chaine', 0, '', $conf->entity); if ($conf->global->TAKEPOS_ORDER_NOTES==1) { @@ -122,6 +127,8 @@ llxHeader('', $langs->trans("CashDeskSetup")); $linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("CashDeskSetup").' (TakePOS)', $linkback, 'title_setup'); +$head = takepos_prepare_head(); +dol_fiche_head($head, 'setup', 'TakePOS', -1); print '
'; @@ -136,6 +143,15 @@ print ''; print ''; print "\n"; +// Terminals +print '\n"; + +// Services if (! empty($conf->service->enabled)) { print '\n"; } + +// Auto print tickets print '\n"; - - +// Root category for products print '
'.$langs->trans("AccountParameter").''.$langs->trans("Value").'
'; $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); print $form->selectarray("STRIPE_LIVE", $arrval, $conf->global->STRIPE_LIVE); } -print '
'; print ''; print '   '.$langs->trans("Example").': pk_test_xxxxxxxxxxxxxxxxxxxxxxxx'; - print '
'; print ''.$langs->trans("STRIPE_TEST_SECRET_KEY").''; print ''; print '   '.$langs->trans("Example").': sk_test_xxxxxxxxxxxxxxxxxxxxxxxx'; - print '
'; print ''.$langs->trans("STRIPE_TEST_WEBHOOK_KEY").''; - print ''; + if ($conf->global->MAIN_FEATURES_LEVEL >= 2) { + print ''; + print '   '.$langs->trans("Example").': we_xxxxxxxxxxxxxxxxxxxxxxxx
'; + } + print ''; print '   '.$langs->trans("Example").': whsec_xxxxxxxxxxxxxxxxxxxxxxxx'; - $out = img_picto('', 'object_globe.png').' '.$langs->trans("ToOfferALinkForTestWebhook").'
'; - $url = dol_buildpath('/public/stripe/ipn.php?test', 2); - $out.= ''; + $out = img_picto('', 'object_globe.png').' '.$langs->trans("ToOfferALinkForTestWebhook").' '; + $url = dol_buildpath('/public/stripe/ipn.php?test', 3); + $out.= ''; $out.= ajax_autoselect("onlinetestwebhookurl", 0); print '
'.$out; - print '
'; + if ($conf->global->MAIN_FEATURES_LEVEL >= 2) + { + if (!empty($conf->global->STRIPE_TEST_WEBHOOK_KEY) && !empty($conf->global->STRIPE_TEST_SECRET_KEY) && !empty($conf->global->STRIPE_TEST_WEBHOOK_ID)) + { + \Stripe\Stripe::setApiKey($conf->global->STRIPE_TEST_SECRET_KEY); + $endpoint = \Stripe\WebhookEndpoint::retrieve($conf->global->STRIPE_TEST_WEBHOOK_ID); + $endpoint->enabled_events = $stripearrayofwebhookevents; + if (GETPOST('webhook', 'alpha') == $conf->global->STRIPE_TEST_WEBHOOK_ID) { + if (empty(GETPOST('status', 'alpha'))) { + $endpoint->disabled = true; + } else { + $endpoint->disabled = false; + } + } + $endpoint->url = dol_buildpath('/public/stripe/ipn.php?test', 3); + $endpoint->save(); + if ($endpoint->status == 'enabled') + { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + } + else + { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + } + //print $endpoint; + } + else + { + print img_picto($langs->trans("inactive"), 'statut5'); + } + } + print'
'.$langs->trans("StripeConnect").''.$langs->trans("StripeConnect_Mode").'
'; @@ -203,7 +249,7 @@ if (empty($conf->stripeconnect->enabled)) print '% + '; print price($conf->global->STRIPE_APPLICATION_FEE); print ' '.$langs->getCurrencySymbol($conf->currency).' '.$langs->trans("minimum").' '.price($conf->global->STRIPE_APPLICATION_FEE_MINIMAL).' '.$langs->getCurrencySymbol($conf->currency).'
'; print ''; print '   '.$langs->trans("Example").': pk_live_xxxxxxxxxxxxxxxxxxxxxxxx'; - print '
'; print ''.$langs->trans("STRIPE_LIVE_SECRET_KEY").''; print ''; print '   '.$langs->trans("Example").': sk_live_xxxxxxxxxxxxxxxxxxxxxxxx'; - print '
'; print ''.$langs->trans("STRIPE_LIVE_WEBHOOK_KEY").''; - print ''; + if ($conf->global->MAIN_FEATURES_LEVEL >= 2) { + print ''; + print '   '.$langs->trans("Example").': we_xxxxxxxxxxxxxxxxxxxxxxxx
'; + } + print ''; print '   '.$langs->trans("Example").': whsec_xxxxxxxxxxxxxxxxxxxxxxxx'; - $out = img_picto('', 'object_globe.png').' '.$langs->trans("ToOfferALinkForLiveWebhook").'
'; - $url = dol_buildpath('/public/stripe/ipn.php', 2); - $out.= ''; + $out = img_picto('', 'object_globe.png').' '.$langs->trans("ToOfferALinkForLiveWebhook").' '; + $url = dol_buildpath('/public/stripe/ipn.php', 3); + $out.= ''; $out.= ajax_autoselect("onlinelivewebhookurl", 0); print '
'.$out; - print '
'; + if ($conf->global->MAIN_FEATURES_LEVEL >= 2) + { + if (!empty($conf->global->STRIPE_LIVE_WEBHOOK_KEY) && !empty($conf->global->STRIPE_LIVE_SECRET_KEY) && !empty($conf->global->STRIPE_LIVE_WEBHOOK_ID)) + { + \Stripe\Stripe::setApiKey($conf->global->STRIPE_LIVE_SECRET_KEY); + $endpoint = \Stripe\WebhookEndpoint::retrieve($conf->global->STRIPE_LIVE_WEBHOOK_ID); + $endpoint->enabled_events = $stripearrayofwebhookevents; + if ( GETPOST('webhook', 'alpha') == $conf->global->STRIPE_LIVE_WEBHOOK_ID ) { + if ( empty(GETPOST('status', 'alpha')) ) { + $endpoint->disabled = true; + } else { + $endpoint->disabled = false; + } + } + $endpoint->url = dol_buildpath('/public/stripe/ipn.php', 3); + $endpoint->save(); + if ($endpoint->status == 'enabled') + { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + } + else + { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + } + //print $endpoint; + } + else + { + print img_picto($langs->trans("inactive"), 'statut5'); + } + } + print '
'.$langs->trans("StripeConnect").''.$langs->trans("StripeConnect_Mode").'
'.$langs->trans("StripeConnect_Mode").'
".img_picto($langs->trans('ShowInStripe'), 'object_globe')." ".$charge->id.""; + print "".img_picto($langs->trans('ShowInStripe'), 'object_globe')." ".$charge->id.""; + print ""; - - if (! empty($conf->stripe->enabled) && !empty($stripeacc)) $connect=$stripeacc.'/'; + if (! empty($conf->stripe->enabled) && !empty($stripeacc)) $connect=$stripeacc.'/'; $url='https://dashboard.stripe.com/'.$connect.'test/customers/'.$charge->customer; if ($servicestatus) { - $url='https://dashboard.stripe.com/'.$connect.'customers/'.$charge->customer; + $url='https://dashboard.stripe.com/'.$connect.'customers/'.$charge->customer; } - print ''.img_picto($langs->trans('ShowInStripe'), 'object_globe').' '.$charge->customer.''; - - print ""; if ($societestatic->id > 0) diff --git a/htdocs/stripe/class/stripe.class.php b/htdocs/stripe/class/stripe.class.php index 8aad2a1b891..ee1c1a67f0a 100644 --- a/htdocs/stripe/class/stripe.class.php +++ b/htdocs/stripe/class/stripe.class.php @@ -140,7 +140,7 @@ class Stripe extends CommonObject if (empty($object->id)) { - dol_syslog("customerStripe is called with param object not loaded"); + dol_syslog("customerStripe is called with the parameter object that is not loaded"); return null; } @@ -238,131 +238,166 @@ class Stripe extends CommonObject } /** - * Get the Stripe payment intent + * Get the Stripe payment intent. Create it with confirm=false * + * @param double $amount Amount + * @param string $currency_code Currency code + * @param string $tag Tag + * @param string $description Description * @param Societe $object Object to pay with Stripe * @param string $customer Stripe customer ref 'cus_xxxxxxxxxxxxx' via customerStripe() * @param string $key ''=Use common API. If not '', it is the Stripe connect account 'acc_....' to use Stripe connect * @param int $status Status (0=test, 1=live) * @param int $usethirdpartyemailforreceiptemail 1=use thirdparty email for receipt - * @param int $mode automatic=automatic payment, manual=need confirmation + * @param int $mode automatic=automatic confirmation/payment when conditions are ok, manual=need to call confirm() on intent + * @param boolean $confirmnow false=default, true=try to confirm immediatly after create (if conditions are ok) * @return \Stripe\PaymentIntent|null Stripe PaymentIntent or null if not found */ - public function getPaymentIntent($object, $customer, $key = null, $status = 0, $usethirdpartyemailforreceiptemail = 0, $mode = 'automatic') + public function getPaymentIntent($amount, $currency_code, $tag, $description = '', $object = null, $customer = null, $key = null, $status = 0, $usethirdpartyemailforreceiptemail = 0, $mode = 'automatic', $confirmnow = false) { global $conf, $user, $mysoc; - if (empty($object->id)) - { - dol_syslog("object not loaded"); - return null; - } + dol_syslog("getPaymentIntent"); $error = 0; if (empty($status)) $service = 'StripeTest'; else $service = 'StripeLive'; + $arrayzerounitcurrency=array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'); + if (! in_array($currency_code, $arrayzerounitcurrency)) $stripeamount = $amount * 100; + else $stripeamount = $amount; + + $fee = round(($$stripeamount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE) * 100); + if ($fee >= ($conf->global->STRIPE_APPLICATION_FEE_MAXIMAL * 100) && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL>$conf->global->STRIPE_APPLICATION_FEE_MINIMAL) { + $fee = round($conf->global->STRIPE_APPLICATION_FEE_MAXIMAL * 100); + } elseif ($fee < ($conf->global->STRIPE_APPLICATION_FEE_MINIMAL * 100)) { + $fee = round($conf->global->STRIPE_APPLICATION_FEE_MINIMAL * 100); + } + $paymentintent = null; - $sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site"; // key_account is cus_.... - $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_facture_demande as pi"; - $sql.= " WHERE pi.fk_facture = " . $object->id; - $sql.= " AND pi.sourcetype = '" . $object->element . "'"; - $sql.= " AND pi.entity IN (".getEntity('societe').")"; - $sql.= " AND pi.ext_payment_site = '" . $service . "'"; - - dol_syslog(get_class($this) . "::customerStripe search stripe customer id for thirdparty id=".$object->id, LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) { - $num = $this->db->num_rows($resql); - if ($num) - { - $obj = $this->db->fetch_object($resql); - $intent = $obj->ext_payment_id; - - dol_syslog(get_class($this) . "::customerStripe found record"); - - // Force to use the correct API key - global $stripearrayofkeysbyenv; - \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']); - - try { - if (empty($key)) { // If the Stripe connect account not set, we use common API usage - $paymentintent = \Stripe\PaymentIntent::retrieve($intent); - } else { - $paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key)); - } - } - catch(Exception $e) - { - $this->error = $e->getMessage(); - } - } - else //if ($createifnotlinkedtostripe) - { - $arrayzerounitcurrency=array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'); - if (! in_array($object->multicurrency_code, $arrayzerounitcurrency)) $stripeamount=$object->multicurrency_total_ttc * 100; - else $stripeamount = $object->multicurrency_total_ttc; - - $fee = round(($object->total_ttc * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE) * 100); - if ($fee >= ($conf->global->STRIPE_APPLICATION_FEE_MAXIMAL * 100) && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL>$conf->global->STRIPE_APPLICATION_FEE_MINIMAL) { - $fee = round($conf->global->STRIPE_APPLICATION_FEE_MAXIMAL * 100); - } elseif ($fee < ($conf->global->STRIPE_APPLICATION_FEE_MINIMAL * 100)) { - $fee = round($conf->global->STRIPE_APPLICATION_FEE_MINIMAL * 100); - } - - $description=$object->element.$object->ref; - - $dataforintent = array( - "amount" => $stripeamount, - "currency" => $object->multicurrency_code, - "customer" => $customer, - "allowed_source_types" => ["card"], - "statement_descriptor" => dol_trunc(dol_trunc(dol_string_unaccent($mysoc->name), 8, 'right', 'UTF-8', 1).' '.$description, 22, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt - "metadata" => array('dol_type'=>$object->element, 'dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR'])) - ); - - if ($conf->entity!=$conf->global->STRIPECONNECT_PRINCIPAL && $fee>0) - { - $dataforintent["application_fee"] = $fee; - } - if ($usethirdpartyemailforreceiptemail && $object->thirdparty->email) - { - $dataforintent["receipt_email"] = $object->thirdparty->email; - } - - try { - // Force to use the correct API key - global $stripearrayofkeysbyenv; - \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']); - - if (empty($key)) { // If the Stripe connect account not set, we use common API usage - $paymentintent = \Stripe\PaymentIntent::create($dataforintent, array("idempotency_key" => "$description")); - } else { - $paymentintent = \Stripe\PaymentIntent::create($dataforintent, array("idempotency_key" => "$description","stripe_account" => $key)); - } - $now=dol_now(); - $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_facture_demande (fk_soc, date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)"; - $sql .= " VALUES ('".$object->socid."','".$this->db->idate($now)."', '0', '".$this->db->escape($paymentintent->id)."', ".$object->id.", '".$object->element."', " . $conf->entity . ", '" . $service . "')"; - $resql = $this->db->query($sql); - if (! $resql) - { - $this->error = $this->db->lasterror(); - dol_syslog(get_class($this) . "::PaymentIntent not insert with id=".$paymentintent->id); - } - } - catch(Exception $e) - { - $this->error = $e->getMessage(); - } - } - } - else + if (is_object($object)) { - dol_print_error($this->db); + $sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site"; + $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_facture_demande as pi"; + $sql.= " WHERE pi.fk_facture = " . $object->id; + $sql.= " AND pi.sourcetype = '" . $object->element . "'"; + $sql.= " AND pi.entity IN (".getEntity('societe').")"; + $sql.= " AND pi.ext_payment_site = '" . $service . "'"; + + dol_syslog(get_class($this) . "::getPaymentIntent search stripe payment intent for object id = ".$object->id, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + if ($num) + { + $obj = $this->db->fetch_object($resql); + $intent = $obj->ext_payment_id; + + dol_syslog(get_class($this) . "::getPaymentIntent found existing payment intent record"); + + // Force to use the correct API key + global $stripearrayofkeysbyenv; + \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']); + + try { + if (empty($key)) { // If the Stripe connect account not set, we use common API usage + $paymentintent = \Stripe\PaymentIntent::retrieve($intent); + } else { + $paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key)); + } + } + catch(Exception $e) + { + $error++; + $this->error = $e->getMessage(); + } + } + } } + if (empty($paymentintent)) + { + $ipaddress=getUserRemoteIP(); + $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress); + if (is_object($object)) + { + $metadata['dol_type'] = $object->element; + $metadata['dol_id'] = $object->id; + } + + $dataforintent = array( + "confirm" => $confirmnow, // Do not confirm immediatly during creation of intent + "confirmation_method" => $mode, + "amount" => $stripeamount, + "currency" => $currency_code, + "payment_method_types" => ["card"], + "description" => $description, + "statement_descriptor" => dol_trunc($tag, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description) + "metadata" => $metadata + ); + if (! is_null($customer)) $dataforintent["customer"]=$customer; + // save_payment_method = true, + // payment_method = + //var_dump($dataforintent); + + if ($conf->entity!=$conf->global->STRIPECONNECT_PRINCIPAL && $fee>0) + { + $dataforintent["application_fee"] = $fee; + } + if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) + { + $dataforintent["receipt_email"] = $object->thirdparty->email; + } + + try { + // Force to use the correct API key + global $stripearrayofkeysbyenv; + \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']); + + if (empty($key)) { // If the Stripe connect account not set, we use common API usage + $paymentintent = \Stripe\PaymentIntent::create($dataforintent, array("idempotency_key" => "$description")); + //$paymentintent = \Stripe\PaymentIntent::create($dataforintent, array()); + } else { + $paymentintent = \Stripe\PaymentIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key)); + //$paymentintent = \Stripe\PaymentIntent::create($dataforintent, array("stripe_account" => $key)); + } + //var_dump($paymentintent); + + // Store the payment intent + if (is_object($object)) + { + $now=dol_now(); + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_facture_demande (fk_soc, date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)"; + $sql .= " VALUES ('".$object->socid."','".$this->db->idate($now)."', '0', '".$this->db->escape($paymentintent->id)."', ".$object->id.", '".$this->db->escape($object->element)."', " . $conf->entity . ", '" . $service . "')"; + $resql = $this->db->query($sql); + if (! $resql) + { + $error++; + $this->error = $this->db->lasterror(); + dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database."); + } + } + else + { + $_SESSION["stripe_payment_intent"] = $paymentintent; + } + } + catch(Exception $e) + { + /*var_dump($dataforintent); + var_dump($description); + var_dump($key); + var_dump($paymentintent); + var_dump($e->getMessage());*/ + $error++; + $this->error = $e->getMessage(); + } + } + + dol_syslog("getPaymentIntent return error=".$error); + return $paymentintent; } @@ -557,7 +592,7 @@ class Stripe extends CommonObject $charge = \Stripe\Charge::create(array( "amount" => "$stripeamount", "currency" => "$currency", - "statement_descriptor" => dol_trunc(dol_trunc(dol_string_unaccent($mysoc->name), 8, 'right', 'UTF-8', 1).' '.$description, 22, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt + "statement_descriptor" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description) "description" => "Stripe payment: ".$description, "capture" => $capture, "metadata" => $metadata, @@ -567,7 +602,7 @@ class Stripe extends CommonObject $paymentarray = array( "amount" => "$stripeamount", "currency" => "$currency", - "statement_descriptor" => dol_trunc(dol_trunc(dol_string_unaccent($mysoc->name), 8, 'right', 'UTF-8', 1).' '.$description, 22, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt + "statement_descriptor" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description) "description" => "Stripe payment: ".$description, "capture" => $capture, "metadata" => $metadata, @@ -580,7 +615,7 @@ class Stripe extends CommonObject $paymentarray["receipt_email"] = $societe->email; } - $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$ref")); + $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description")); } } else { $fee = round(($object->total_ttc * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE) * 100); @@ -594,7 +629,7 @@ class Stripe extends CommonObject $paymentarray = array( "amount" => "$stripeamount", "currency" => "$currency", - "statement_descriptor" => dol_trunc(dol_trunc(dol_string_unaccent($mysoc->name), 8, 'right', 'UTF-8', 1).' '.$description, 22, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt + "statement_descriptor" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description) "description" => "Stripe payment: ".$description, "capture" => $capture, "metadata" => $metadata, @@ -610,7 +645,7 @@ class Stripe extends CommonObject $paymentarray["receipt_email"] = $societe->email; } - $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$ref","stripe_account" => "$account")); + $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description", "stripe_account" => "$account")); } if (isset($charge->id)) {} diff --git a/htdocs/stripe/config.php b/htdocs/stripe/config.php index fb0ef25f8bd..5638a10d332 100644 --- a/htdocs/stripe/config.php +++ b/htdocs/stripe/config.php @@ -55,4 +55,4 @@ else \Stripe\Stripe::setApiKey($stripearrayofkeys['secret_key']); \Stripe\Stripe::setAppInfo("Dolibarr Stripe", DOL_VERSION, "https://www.dolibarr.org"); // add dolibarr version -\Stripe\Stripe::setApiVersion("2018-11-08"); // force version API +\Stripe\Stripe::setApiVersion("2019-03-14"); // force version API diff --git a/htdocs/stripe/lib/stripe.lib.php b/htdocs/stripe/lib/stripe.lib.php index d806d6a39b3..100ce840e24 100644 --- a/htdocs/stripe/lib/stripe.lib.php +++ b/htdocs/stripe/lib/stripe.lib.php @@ -87,12 +87,12 @@ function showStripePaymentUrl($type, $ref) * @param string $freetag Free tag * @return string Url string */ -function getStripePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag = 'your_free_tag') +function getStripePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag = 'your_tag') { global $conf; $ref=str_replace(' ', '', $ref); - + if ($type == 'free') { $out=DOL_MAIN_URL_ROOT.'/public/stripe/newpayment.php?amount='.($mode?'':'').$amount.($mode?'':'').'&tag='.($mode?'':'').$freetag.($mode?'':''); diff --git a/htdocs/supplier_proposal/card.php b/htdocs/supplier_proposal/card.php index c87411cfb83..f52a846f4c2 100644 --- a/htdocs/supplier_proposal/card.php +++ b/htdocs/supplier_proposal/card.php @@ -136,7 +136,7 @@ if (empty($reshook)) else { if ($object->id > 0) { - $result = $object->createFromClone($socid); + $result = $object->createFromClone($user, $socid); if ($result > 0) { header("Location: " . $_SERVER['PHP_SELF'] . '?id=' . $result); exit(); @@ -1084,7 +1084,7 @@ if ($action == 'create') } else { print ''; print $form->select_company('', 'socid', 's.fournisseur = 1', 'SelectThirdParty', 0, 0, null, 0, 'minwidth300'); - print ' '.$langs->trans("AddThirdParty").''; + print ' '.$langs->trans("AddThirdParty").''; print '
'.$langs->trans("Parameters").''.$langs->trans("Value").'
'; +print $langs->trans("NumberOfTerminals"); +print ''; +$array=array(1=>"1", 2=>"2", 3=>"3", 4=>"4", 5=>"5", 6=>"6", 7=>"7", 8=>"8", 9=>"9"); +print $form->selectarray('TAKEPOS_NUM_TERMINALS', $array, (empty($conf->global->TAKEPOS_NUM_TERMINALS)?'0':$conf->global->TAKEPOS_NUM_TERMINALS), 0); +print "
'; @@ -144,14 +160,15 @@ if (! empty($conf->service->enabled)) print $form->selectyesno("CASHDESK_SERVICES", $conf->global->CASHDESK_SERVICES, 1); print "
'; print $langs->trans("AutoPrintTickets"); print ''; print $form->selectyesno("TAKEPOS_AUTO_PRINT_TICKETS", $conf->global->TAKEPOS_AUTO_PRINT_TICKETS, 1); print "
'; print $form->textwithpicto($langs->trans("RootCategoryForProductsToSell"), $langs->trans("RootCategoryForProductsToSellDesc")); print ''; @@ -253,10 +270,11 @@ print '
'; print '
'; - +/* print ''; print ''; -print ''; +if (!$conf->global->TAKEPOS_NUM_TERMINALS || $conf->global->TAKEPOS_NUM_TERMINALS=="1") print ''; +else print ''; print "\n"; print ''; @@ -283,7 +301,7 @@ if (! empty($conf->banque->enabled)) $name="CASHDESK_ID_BANKACCOUNT_".$modep->code; print ''; print ''; } @@ -326,6 +344,7 @@ if (! empty($conf->stock->enabled)) print '
'.$langs->trans("Terminal").' 0'.$langs->trans("Value").''.$langs->trans("Parameters").''.$langs->trans("Value").''.$langs->trans("Terminal").' 1'.$langs->trans("Value").'
'.$langs->trans("CashDeskThirdPartyForSell").'
'.$langs->trans("CashDeskBankAccountFor").' '.$langs->trans($modep->libelle).''; - $cour=preg_match('/^LIQ.*/', $modep->code)?2:1; + $cour=preg_match('|^LIQ.*|', $modep->code)?2:1; $form->select_comptes($conf->global->$name, $name, 0, "courant=".$cour, 1); print '
'; print '
'; +*/ print '
'; diff --git a/htdocs/takepos/admin/terminal.php b/htdocs/takepos/admin/terminal.php new file mode 100644 index 00000000000..7085d904891 --- /dev/null +++ b/htdocs/takepos/admin/terminal.php @@ -0,0 +1,204 @@ + + * Copyright (C) 2011-2017 Juanjo Menent + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/takepos/admin/setup.php + * \ingroup takepos + * \brief Setup page for TakePos module + */ + +require '../../main.inc.php'; // Load $user and permissions +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; +require_once DOL_DOCUMENT_ROOT."/core/lib/takepos.lib.php"; + +$terminal=GETPOST('terminal', 'int'); +// If socid provided by ajax company selector +if (! empty($_REQUEST['CASHDESK_ID_THIRDPARTY'.$terminal.'_id'])) +{ + $_GET['CASHDESK_ID_THIRDPARTY'.$terminal] = GETPOST('CASHDESK_ID_THIRDPARTY'.$terminal.'_id', 'alpha'); + $_POST['CASHDESK_ID_THIRDPARTY'.$terminal] = GETPOST('CASHDESK_ID_THIRDPARTY'.$terminal.'_id', 'alpha'); + $_REQUEST['CASHDESK_ID_THIRDPARTY'.$terminal] = GETPOST('CASHDESK_ID_THIRDPARTY'.$terminal.'_id', 'alpha'); +} + +// Security check +if (!$user->admin) accessforbidden(); + +$langs->loadLangs(array("admin", "cashdesk")); + +global $db; + +$sql = "SELECT code, libelle FROM ".MAIN_DB_PREFIX."c_paiement"; +$sql.= " WHERE entity IN (".getEntity('c_paiement').")"; +$sql.= " AND active = 1"; +$sql.= " ORDER BY libelle"; +$resql = $db->query($sql); +$paiements = array(); +if($resql){ + while ($obj = $db->fetch_object($resql)){ + array_push($paiements, $obj); + } +} + +$terminaltouse = $terminal; +if ($terminaltouse == '1') $terminaltouse = ''; + + +/* + * Actions + */ + +if (GETPOST('action', 'alpha') == 'set') +{ + $db->begin(); + if (GETPOST('socid', 'int') < 0) $_POST["socid"]=''; + + $res = dolibarr_set_const($db, "CASHDESK_ID_THIRDPARTY".$terminaltouse, (GETPOST('socid', 'int') > 0 ? GETPOST('socid', 'int') : ''), 'chaine', 0, '', $conf->entity); + + $res = dolibarr_set_const($db, "CASHDESK_ID_BANKACCOUNT_CASH".$terminaltouse, (GETPOST('CASHDESK_ID_BANKACCOUNT_CASH'.$terminaltouse, 'alpha') > 0 ? GETPOST('CASHDESK_ID_BANKACCOUNT_CASH'.$terminaltouse, 'alpha') : ''), 'chaine', 0, '', $conf->entity); + $res = dolibarr_set_const($db, "CASHDESK_ID_BANKACCOUNT_CHEQUE".$terminaltouse, (GETPOST('CASHDESK_ID_BANKACCOUNT_CHEQUE'.$terminaltouse, 'alpha') > 0 ? GETPOST('CASHDESK_ID_BANKACCOUNT_CHEQUE'.$terminaltouse, 'alpha') : ''), 'chaine', 0, '', $conf->entity); + $res = dolibarr_set_const($db, "CASHDESK_ID_BANKACCOUNT_CB".$terminaltouse, (GETPOST('CASHDESK_ID_BANKACCOUNT_CB'.$terminaltouse, 'alpha') > 0 ? GETPOST('CASHDESK_ID_BANKACCOUNT_CB'.$terminaltouse, 'alpha') : ''), 'chaine', 0, '', $conf->entity); + foreach($paiements as $modep) { + if (in_array($modep->code, array('LIQ', 'CB', 'CHQ'))) continue; + $name="CASHDESK_ID_BANKACCOUNT_".$modep->code.$terminaltouse; + $res = dolibarr_set_const($db, $name, (GETPOST($name, 'alpha') > 0 ? GETPOST($name, 'alpha') : ''), 'chaine', 0, '', $conf->entity); + } + $res = dolibarr_set_const($db, "CASHDESK_ID_WAREHOUSE".$terminaltouse, (GETPOST('CASHDESK_ID_WAREHOUSE'.$terminaltouse, 'alpha') > 0 ? GETPOST('CASHDESK_ID_WAREHOUSE'.$terminaltouse, 'alpha') : ''), 'chaine', 0, '', $conf->entity); + $res = dolibarr_set_const($db, "CASHDESK_NO_DECREASE_STOCK".$terminaltouse, GETPOST('CASHDESK_NO_DECREASE_STOCK'.$terminaltouse, 'alpha'), 'chaine', 0, '', $conf->entity); + + dol_syslog("admin/cashdesk: level ".GETPOST('level', 'alpha')); + + if (! $res > 0) $error++; + + if (! $error) + { + $db->commit(); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + } + else + { + $db->rollback(); + setEventMessages($langs->trans("Error"), null, 'errors'); + } +} + + +/* + * View + */ + +$form=new Form($db); +$formproduct=new FormProduct($db); + +llxHeader('', $langs->trans("CashDeskSetup")); + +$linkback=''.$langs->trans("BackToModuleList").''; +print load_fiche_titre($langs->trans("CashDeskSetup").' (TakePOS)', $linkback, 'title_setup'); +$head = takepos_prepare_head(); +dol_fiche_head($head, 'terminal'.$terminal, 'TakePOS', -1); +print '
'; + + +// Mode +print '
'; +print ''; +print ''; + +print ''; +print ''; +print ''; +print "\n"; + +print ''; +print ''; +if (! empty($conf->banque->enabled)) +{ + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + foreach($paiements as $modep) { + if (in_array($modep->code, array('LIQ', 'CB', 'CHQ'))) continue; + $name="CASHDESK_ID_BANKACCOUNT_".$modep->code.$terminaltouse; + print ''; + print ''; + } +} + +if (! empty($conf->stock->enabled)) +{ + + print ''; // Force warehouse (this is not a default value) + print ''; + + $disabled=$conf->global->{'CASHDESK_NO_DECREASE_STOCK'.$terminal}; + + + print ''; // Force warehouse (this is not a default value) + print ''; +} + +print '
'.$langs->trans("Parameters").''.$langs->trans("Value").'
'.$langs->trans("CashDeskThirdPartyForSell").''; +print $form->select_company($conf->global->{'CASHDESK_ID_THIRDPARTY'.$terminaltouse}, 'socid', 's.client in (1, 3) AND s.status = 1', 1, 0, 0, array(), 0); +print '
'.$langs->trans("CashDeskBankAccountForSell").''; + $form->select_comptes($conf->global->{'CASHDESK_ID_BANKACCOUNT_CASH'.$terminaltouse}, 'CASHDESK_ID_BANKACCOUNT_CASH'.$terminaltouse, 0, "courant=2", 1); + print '
'.$langs->trans("CashDeskBankAccountForCheque").''; + $form->select_comptes($conf->global->{'CASHDESK_ID_BANKACCOUNT_CHEQUE'.$terminaltouse}, 'CASHDESK_ID_BANKACCOUNT_CHEQUE'.$terminaltouse, 0, "courant=1", 1); + print '
'.$langs->trans("CashDeskBankAccountForCB").''; + $form->select_comptes($conf->global->{'CASHDESK_ID_BANKACCOUNT_CB'.$terminaltouse}, 'CASHDESK_ID_BANKACCOUNT_CB'.$terminaltouse, 0, "courant=1", 1); + print '
'.$langs->trans("CashDeskBankAccountFor").' '.$langs->trans($modep->libelle).''; + $cour=preg_match('/^LIQ.*/', $modep->code)?2:1; + $form->select_comptes($conf->global->$name, $name, 0, "courant=".$cour, 1); + print '
'.$langs->trans("CashDeskDoNotDecreaseStock").''; + if (empty($conf->productbatch->enabled)) { + print $form->selectyesno('CASHDESK_NO_DECREASE_STOCK'.$terminal, $conf->global->{'CASHDESK_NO_DECREASE_STOCK'.$terminal}, 1); + } + else + { + if (!$conf->global->{'CASHDESK_NO_DECREASE_STOCK'.$terminal}) { + $res = dolibarr_set_const($db, "CASHDESK_NO_DECREASE_STOCK".$terminal, 1, 'chaine', 0, '', $conf->entity); + } + print $langs->trans("Yes").'
'; + print ''.$langs->trans('StockDecreaseForPointOfSaleDisabledbyBatch').''; + } + print '
'.$langs->trans("CashDeskIdWareHouse").''; + if (! $disabled) + { + print $formproduct->selectWarehouses($conf->global->{'CASHDESK_ID_WAREHOUSE'.$terminal}, 'CASHDESK_ID_WAREHOUSE'.$terminal, '', 1, $disabled); + print ' ('.$langs->trans("Create").')'; + } + else + { + print ''.$langs->trans("StockDecreaseForPointOfSaleDisabled").''; + } + print '
'; +print '
'; + +print '
'; + +print "
\n"; + +print '
'; + +llxFooter(); +$db->close(); diff --git a/htdocs/takepos/invoice.php b/htdocs/takepos/invoice.php index d30adb72ef0..7645ef176c5 100644 --- a/htdocs/takepos/invoice.php +++ b/htdocs/takepos/invoice.php @@ -35,6 +35,7 @@ if (!defined('NOREQUIREAJAX')) { define('NOREQUIREAJAX', '1'); } require '../main.inc.php'; require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php'; require_once DOL_DOCUMENT_ROOT . '/compta/paiement/class/paiement.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php'; $langs->loadLangs(array("bills", "cashdesk")); @@ -89,7 +90,7 @@ if ($invoiceid > 0) } else { - $ret = $invoice->fetch('', '(PROV-POS-'.$place.')'); + $ret = $invoice->fetch('', '(PROV-POS'.$_SESSION["takepostermvar"].'-'.$place.')'); } if ($ret > 0) { @@ -103,12 +104,12 @@ if ($ret > 0) if ($action == 'valid' && $user->rights->facture->creer) { - if ($pay == "cash") $bankaccount = $conf->global->CASHDESK_ID_BANKACCOUNT_CASH; // For backward compatibility - elseif ($pay == "card") $bankaccount = $conf->global->CASHDESK_ID_BANKACCOUNT_CB; // For backward compatibility - elseif ($pay == "cheque") $bankaccount = $conf->global->CASHDESK_ID_BANKACCOUNT_CHEQUE; // For backward compatibility + if ($pay == "cash") $bankaccount = $conf->global->{'CASHDESK_ID_BANKACCOUNT_CASH'.$_SESSION["takepostermvar"]}; // For backward compatibility + elseif ($pay == "card") $bankaccount = $conf->global->{'CASHDESK_ID_BANKACCOUNT_CB'.$_SESSION["takepostermvar"]}; // For backward compatibility + elseif ($pay == "cheque") $bankaccount = $conf->global->{'CASHDESK_ID_BANKACCOUNT_CHEQUE'.$_SESSION["takepostermvar"]}; // For backward compatibility else { - $accountname="CASHDESK_ID_BANKACCOUNT_".$pay; + $accountname="CASHDESK_ID_BANKACCOUNT_".$pay.$_SESSION["takepostermvar"]; $bankaccount=$conf->global->$accountname; } $now=dol_now(); @@ -136,9 +137,9 @@ if ($action == 'valid' && $user->rights->facture->creer) $invoice->update($user); } - if (! empty($conf->stock->enabled) && $conf->global->CASHDESK_NO_DECREASE_STOCK != "1") + if (! empty($conf->stock->enabled) && $conf->global->{'CASHDESK_NO_DECREASE_STOCK'.$_SESSION["takepostermvar"]} != "1") { - $invoice->validate($user, '', $conf->global->CASHDESK_ID_WAREHOUSE); + $invoice->validate($user, '', $conf->global->{'CASHDESK_ID_WAREHOUSE'.$_SESSION["takepostermvar"]}); } else { @@ -170,15 +171,22 @@ if ($action == 'valid' && $user->rights->facture->creer) } } +if ($action == 'history') +{ + $placeid = GETPOST('placeid', 'int'); + $invoice = new Facture($db); + $invoice->fetch($placeid); +} + if (($action=="addline" || $action=="freezone") && $placeid == 0) { - $invoice->socid = $conf->global->CASHDESK_ID_THIRDPARTY; + $invoice->socid = $conf->global->{'CASHDESK_ID_THIRDPARTY'.$_SESSION["takepostermvar"]}; $invoice->date = dol_now(); $invoice->module_source = 'takepos'; $invoice->pos_source = (string) $posnb; $placeid = $invoice->create($user); - $sql="UPDATE ".MAIN_DB_PREFIX."facture set ref='(PROV-POS-".$place.")' where rowid=".$placeid; + $sql="UPDATE ".MAIN_DB_PREFIX."facture set ref='(PROV-POS".$_SESSION["takepostermvar"]."-".$place.")' where rowid=".$placeid; $db->query($sql); } @@ -335,7 +343,7 @@ if ($action == "order" and $placeid != 0) } $sectionwithinvoicelink=''; -if ($action=="valid") +if ($action=="valid" || $action=="history") { $sectionwithinvoicelink.=''."\n"; $sectionwithinvoicelink.=''; @@ -352,7 +360,7 @@ if ($action=="valid") else $sectionwithinvoicelink.=$langs->trans('BillShortStatusValidated'); } $sectionwithinvoicelink.=''; - if ($conf->global->TAKEPOSCONNECTOR) $sectionwithinvoicelink.=' '; + if ($conf->global->TAKEPOSCONNECTOR) $sectionwithinvoicelink.=' '; else $sectionwithinvoicelink.=' '; if ($conf->global->TAKEPOS_AUTO_PRINT_TICKETS) $sectionwithinvoicelink.=''; } @@ -551,11 +559,11 @@ else { // No invoice generated yet print ''; -if ($invoice->socid != $conf->global->CASHDESK_ID_THIRDPARTY) +if ($invoice->socid != $conf->global->{'CASHDESK_ID_THIRDPARTY'.$_SESSION["takepostermvar"]}) { $soc = new Societe($db); if ($invoice->socid > 0) $soc->fetch($invoice->socid); - else $soc->fetch($conf->global->CASHDESK_ID_THIRDPARTY); + else $soc->fetch($conf->global->{'CASHDESK_ID_THIRDPARTY'.$_SESSION["takepostermvar"]}); print '

'; print $langs->trans("Customer").': '.$soc->name; print '

'; diff --git a/htdocs/takepos/pay.php b/htdocs/takepos/pay.php index 1fe325dfdeb..74af8f62ae9 100644 --- a/htdocs/takepos/pay.php +++ b/htdocs/takepos/pay.php @@ -51,7 +51,7 @@ if ($invoiceid > 0) } else { - $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."facture where ref='(PROV-POS-".$place.")'"; + $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."facture where ref='(PROV-POS".$_SESSION["takepostermvar"]."-".$place.")'"; $resql = $db->query($sql); $obj = $db->fetch_object($resql); if ($obj) diff --git a/htdocs/takepos/receipt.php b/htdocs/takepos/receipt.php index 7f3d5bdc0cc..c80b3b5b932 100644 --- a/htdocs/takepos/receipt.php +++ b/htdocs/takepos/receipt.php @@ -44,7 +44,7 @@ top_httphead('text/html'); if ($place > 0) { - $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."facture where ref='(PROV-POS-".$place.")'"; + $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."facture where ref='(PROV-POS".$_SESSION["takepostermvar"]."-".$place.")'"; $resql = $db->query($sql); $obj = $db->fetch_object($resql); if ($obj) diff --git a/htdocs/takepos/takepos.php b/htdocs/takepos/takepos.php index 49a240cee1c..f5de960223d 100644 --- a/htdocs/takepos/takepos.php +++ b/htdocs/takepos/takepos.php @@ -40,8 +40,15 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; $place = (GETPOST('place', 'int') > 0 ? GETPOST('place', 'int') : 0); // $place is id of table for Ba or Restaurant $posnb = (GETPOST('posnb', 'int') > 0 ? GETPOST('posnb', 'int') : 0); // $posnb is id of POS - $action = GETPOST('action', 'alpha'); +$setterminal = GETPOST('setterminal', 'int'); + +if ($setterminal>0) +{ + $_SESSION["takeposterminal"]=$setterminal; + if ($setterminal==1) $_SESSION["takepostermvar"]=""; + else $_SESSION["takepostermvar"]=$setterminal; +} $langs->loadLangs(array("bills","orders","commercial","cashdesk","receiptprinter")); @@ -55,7 +62,7 @@ if ($conf->browser->layout == 'phone') $maxproductbydefaultforthisdevice=16; } $MAXCATEG = (empty($conf->global->TAKEPOS_NB_MAXCATEG)?$maxcategbydefaultforthisdevice:$conf->global->TAKEPOS_NB_MAXCATEG); -$MAXPRODUCT = (empty($conf->global->TAKEPOS_NB_MAXPRODUCT)?$maxproductbydefaultforthisdevice:$conf->global->TAKEPOS_NB_MAXPRODUCT);; +$MAXPRODUCT = (empty($conf->global->TAKEPOS_NB_MAXPRODUCT)?$maxproductbydefaultforthisdevice:$conf->global->TAKEPOS_NB_MAXPRODUCT); /* @@ -77,7 +84,7 @@ top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss); - +global->TAKEPOS_NUM_TERMINALS!="1" && $_SESSION["takeposterminal"]=="") print '
'.$langs->trans('TerminalSelect').'
'; +?>
@@ -607,6 +652,7 @@ else } $menus[$r++]=array('title'=>'
'.$langs->trans("Customer").'
', 'action'=>'Customer();'); +$menus[$r++]=array('title'=>'
'.$langs->trans("History").'
', 'action'=>'History();'); $menus[$r++]=array('title'=>'
'.$langs->trans("FreeZone").'
', 'action'=>'FreeZone();'); $menus[$r++]=array('title'=>'
'.$langs->trans("Payment").'
', 'action'=>'CloseBill();'); diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index fba05a2f26f..b797ec69b9e 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -91,6 +91,9 @@ input, select { margin-bottom:1px; margin-top:1px; } +input.button.massactionconfirmed { + margin: 4px; +} /* Focus definitions must be after standard definition */ textarea:focus { @@ -129,19 +132,27 @@ input.smallpadd { /* Used for timesheet input */ input.buttongen { vertical-align: middle; } -input.buttonpayment { +input.buttonpayment, button.buttonpayment, div.buttonpayment { min-width: 320px; margin-bottom: 15px; background-image: none; line-height: 24px; padding: 8px; background: none; - padding-left: 38px; - text-align: ; - border: 1px solid #ddd; - background-color: #eee; + text-align: center; + border: 0; + background-color: #9999bb; white-space: normal; - box-shadow: 1px 1px 8px #bbb; + box-shadow: 1px 1px 4px #bbb; + color: #fff; + border-radius: 4px; +} +div.buttonpayment input { + background-color: unset; + color: #fff; + border-bottom: unset; + font-weight: bold; + text-transform: uppercase; } input.buttonpaymentcb { background-image: url(); @@ -307,6 +318,13 @@ hr { border: 0; border-top: 1px solid #ccc; } box-shadow: none; -webkit-box-shadow: none; } +.button_search, .button_removefilter { + border: unset; + background-color: unset; +} +.button_search:hover, .button_removefilter:hover { + cursor: pointer; +} form { padding:0px; margin:0px; @@ -436,7 +454,15 @@ textarea.centpercent { .cursornotallowed { cursor: not-allowed; } +.backgroundblank { + background-color: #fff; +} +.checkboxattachfilelabel { + font-size: 0.85em; + opacity: 0.7; +} +/* Themes for badges */ .borderrightlight @@ -1421,8 +1447,7 @@ li.menuhider:hover { background-image: none !important; } li.tmenusel, li.tmenu:hover { - background: rgba(0, 0, 0, 0.1); - /* background: rgb(); */ + /* background: rgba(0, 0, 0, 0.1); */ } li.tmenusel::after, li.tmenu:hover::after{ @@ -1990,7 +2015,7 @@ input.vmenusearchselectcombo[type=text] { .searchform input { font-size: 16px; } -a.vmenu:link, a.vmenu:visited, a.vmenu:hover, a.vmenu:active, span.vmenu { white-space: nowrap; font-family: ; text-align: ; font-weight: bold; } /* bold = 600, 500 is ko with Edge on 1200x960 */ +a.vmenu:link, a.vmenu:visited, a.vmenu:hover, a.vmenu:active, span.vmenu, span.vsmenu { white-space: nowrap; font-family: ; text-align: ; font-weight: bold; } /* bold = 600, 500 is ko with Edge on 1200x960 */ font.vmenudisabled { font-family: ; text-align: ; font-weight: bold; color: #aaa; margin-left: 4px; } /* bold = 600, 500 is ko with Edge on 1200x960 */ a.vmenu:link, a.vmenu:visited { color: #; } @@ -2215,6 +2240,10 @@ div.tabBar table.tableforservicepart2:last-child { height: unset; padding-top: 0 !important; } +/* Payment Screen : Pointer cursor in the autofill image */ +.AutoFillAmount { + cursor:pointer; +} div.popuptabset { padding: 6px; @@ -2587,12 +2616,17 @@ table.liste, table.noborder, table.formdoc, div.noborder { margin: 0px 0px 5px 0px; } -table.liste tr:last-of-type td, table.noborder tr:last-of-type td, table.formdoc tr:last-of-type td, div.noborder tr:last-of-type td { +#tablelines { border-bottom-width: 1px; border-bottom-color: rgb(); border-bottom-style: solid; } -div.tabBar div.ficheaddleft table.noborder:last-of-type { +table.liste tr:last-of-type td, table.noborder:not(#tablelines) tr:last-of-type td, table.formdoc tr:last-of-type td, div.noborder tr:last-of-type td { + border-bottom-width: 1px; + border-bottom-color: rgb(); + border-bottom-style: solid; +} +div.tabBar div.ficheaddleft table.noborder:not(.margintable):not(.paymenttable):last-of-type { border-bottom: 1px solid rgb(); } div.tabBar table.border>tbody>tr:last-of-type>td { @@ -2611,20 +2645,25 @@ table.paddingtopbottomonly tr td { .liste_titre_filter { background: rgb() !important; } +table:not(.listwithfilterbefore) tr.liste_titre_filter:first-of-type td.liste_titre { + padding-top: 5px; +} + tr.liste_titre_filter td.liste_titre { /* border-bottom: 1px solid #ddd; */ padding-top: 1px; padding-bottom: 0px; } +tr.liste_titre_filter td.liste_titre:first-of-type { +/* height: 36px; */ +} .liste_titre_create td, .liste_titre_create th, .liste_titre_create .tagtd { + border-bottom-width: 0 !important; border-top-width: 1px; border-top-color: rgb(); border-top-style: solid; } -/*.liste_titre_create td.nobottom, tr#trlinefordates td { - background-color: rgb() !important; -}*/ tr#trlinefordates td { border-bottom: 0px !important; } @@ -2846,13 +2885,19 @@ div.pagination li.paginationafterarrows { /* Set the color for hover lines */ .oddeven:hover, .evenodd:hover, .impair:hover, .pair:hover { - background: rgb() !important; /* Must be background to be stronger than background of odd or even */ - +} +.tredited, .tredited td { + background: rgb() !important; /* Must be background to be stronger than background of odd or even */ + border-bottom: 0 !important; +} +.treditedlinefordate { + background: rgb() !important; /* Must be background to be stronger than background of odd or even */ + border-bottom: 0px; } .highlight { - background: rgb() !important; /* Must be background to be stronger than background of odd or even */ + background: rgb() !important; /* Must be background to be stronger than background of odd or even */ } @@ -2917,6 +2962,12 @@ form.pair, form.impair { form.tagtr:last-of-type div.tagtd, tr.pair:last-of-type td, tr.impair:last-of-type td { border-bottom: 0px !important; } +tr.nobottom td { + border-bottom: 0px !important; +} +div.tableforcontact form.tagtr:last-of-type div.tagtd { + border-bottom: 1px solid #ddd !important; +} tr.pair td .nobordernopadding tr td, tr.impair td .nobordernopadding tr td { border-bottom: 0px !important; } @@ -2924,6 +2975,9 @@ table.nobottomiftotal tr.liste_total td { background-color: #fff; border-bottom: 0px !important; } +table.nobottom, td.nobottom { + border-bottom: 0px !important; +} div.liste_titre .tagtd { vertical-align: middle; } @@ -3099,7 +3153,7 @@ div:not(.fichecenter):not(.fichehalfleft):not(.ficheaddleft) .oddeven.tagtr:nth- .noborder > tbody > tr:nth-child(even):not(:last-child) td:not(.liste_titre), .liste > tbody > tr:nth-child(even):not(:last-child) td:not(.liste_titre), .noborder .oddeven.tagtr:nth-child(even):not(:last-child) .tagtd:not(.liste_titre) { - border-bottom: 1px solid #ddd; + border-bottom: 1px solid #e0e0e0; } .noborder > tbody > tr:nth-child(odd):not(.liste_titre), .liste > tbody > tr:nth-child(odd):not(.liste_titre), @@ -3115,7 +3169,7 @@ div:not(.fichecenter):not(.fichehalfleft):not(.ficheaddleft) .oddeven.tagtr:nth- .noborder > tbody > tr:nth-child(odd):not(:last-child) td:not(.liste_titre), .liste > tbody > tr:nth-child(odd):not(:last-child) td:not(.liste_titre), .noborder .oddeven.tagtr:nth-child(odd):not(:last-child) .tagtd:not(.liste_titre) { - border-bottom: 1px solid #ddd; + border-bottom: 1px solid #e0e0e0; } ul.noborder li:nth-child(even):not(.liste_titre) { @@ -3161,7 +3215,10 @@ ul.noborder li:nth-child(even):not(.liste_titre) { padding: 3px; } .boxstats { - padding: 3px; + padding-left: 3px; + padding-right: 3px; + padding-top: 2px; + padding-bottom: 2px; width: 121px; } .boxstatscontent { @@ -3367,7 +3424,7 @@ img.boxhandle, img.boxclose { */ .ok { color: #114466; } -.warning { color: #887711; } +.warning { color: #887711 !important; } .error { color: #550000 !important; font-weight: bold; } div.ok { @@ -3532,6 +3589,7 @@ div#card-errors { color: #fa755a; text-align: center; padding-top: 3px; + max-width: 320px; } @@ -4116,6 +4174,10 @@ A.none, A.none:active, A.none:visited, A.none:hover { /* CKEditor */ /* ============================================================================== */ +body.cke_show_borders { + margin: 5px !important; +} + .cke_dialog { border: 1px #bbb solid ! important; } @@ -5599,7 +5661,7 @@ div.tabsElem a.tab { /* nboftopmenuentries = , fontsize= */ /* rule to reduce top menu - 1st reduction: Reduce width of top menu icons */ -@media only screen and (max-width: px) /* reduction 1 */ +@media only screen and (max-width: global->THEME_ELDY_WITDHOFFSET_FOR_REDUC1) ? round($nbtopmenuentries * 90, 0) + 240 : $conf->global->THEME_ELDY_WITDHOFFSET_FOR_REDUC1; ?>px) /* reduction 1 */ { div.tmenucenter { width: px; /* size of viewport */ @@ -5632,7 +5694,7 @@ div.tabsElem a.tab { } } /* rule to reduce top menu - 2nd reduction: Reduce width of top menu icons again */ -@media only screen and (max-width: px) /* reduction 2 */ +@media only screen and (max-width: global->THEME_ELDY_WITDHOFFSET_FOR_REDUC2) ? round($nbtopmenuentries * 69, 0) + 40 : $conf->global->THEME_ELDY_WITDHOFFSET_FOR_REDUC2; ?>px) /* reduction 2 */ { div.mainmenu { height: 23px; @@ -5656,7 +5718,7 @@ div.tabsElem a.tab { } } /* rule to reduce top menu - 3rd reduction: The menu for user is on left */ -@media only screen and (max-width: px) /* reduction 3 */ +@media only screen and (max-width: global->THEME_ELDY_WITDHOFFSET_FOR_REDUC3) ? round($nbtopmenuentries * 47, 0) + 40 : $conf->global->THEME_ELDY_WITDHOFFSET_FOR_REDUC3; ?>px) /* reduction 3 */ { .side-nav { z-index: 200; diff --git a/htdocs/theme/eldy/img/object_bom.png b/htdocs/theme/eldy/img/object_bom.png new file mode 100644 index 00000000000..38b59646d71 Binary files /dev/null and b/htdocs/theme/eldy/img/object_bom.png differ diff --git a/htdocs/theme/eldy/style.css.php b/htdocs/theme/eldy/style.css.php index 8087c955592..70479584c5a 100644 --- a/htdocs/theme/eldy/style.css.php +++ b/htdocs/theme/eldy/style.css.php @@ -134,15 +134,14 @@ $fontsize =empty($user->conf->THEME_ELDY_ENABLE_PERSONALIZED)?(empty( $fontsizesmaller =empty($user->conf->THEME_ELDY_ENABLE_PERSONALIZED)?(empty($conf->global->THEME_ELDY_FONT_SIZE2) ?$fontsize:$conf->global->THEME_ELDY_FONT_SIZE2) :(empty($user->conf->THEME_ELDY_FONT_SIZE2)?$fontsize:$user->conf->THEME_ELDY_FONT_SIZE2); // Hover color -$colorbacklinepairhover=((! isset($conf->global->THEME_ELDY_USE_HOVER) || (string) $conf->global->THEME_ELDY_USE_HOVER === '0')?'':($conf->global->THEME_ELDY_USE_HOVER === '1'?'e6edf0':$conf->global->THEME_ELDY_USE_HOVER)); -$colorbacklinepairchecked=((! isset($conf->global->THEME_ELDY_USE_CHECKED) || (string) $conf->global->THEME_ELDY_USE_CHECKED === '0')?'':($conf->global->THEME_ELDY_USE_CHECKED === '1'?'e6edf0':$conf->global->THEME_ELDY_USE_CHECKED)); +$colorbacklinepairhover=((! isset($conf->global->THEME_ELDY_USE_HOVER) || (string) $conf->global->THEME_ELDY_USE_HOVER === '255,255,255')?'':($conf->global->THEME_ELDY_USE_HOVER === '1'?'e6edf0':$conf->global->THEME_ELDY_USE_HOVER)); +$colorbacklinepairchecked=((! isset($conf->global->THEME_ELDY_USE_CHECKED) || (string) $conf->global->THEME_ELDY_USE_CHECKED === '255,255,255')?'':($conf->global->THEME_ELDY_USE_CHECKED === '1'?'e6edf0':$conf->global->THEME_ELDY_USE_CHECKED)); if (! empty($user->conf->THEME_ELDY_ENABLE_PERSONALIZED)) { $colorbacklinepairhover=((! isset($user->conf->THEME_ELDY_USE_HOVER) || $user->conf->THEME_ELDY_USE_HOVER === '0')?'':($user->conf->THEME_ELDY_USE_HOVER === '1'?'e6edf0':$user->conf->THEME_ELDY_USE_HOVER)); $colorbacklinepairchecked=((! isset($user->conf->THEME_ELDY_USE_CHECKED) || $user->conf->THEME_ELDY_USE_CHECKED === '0')?'':($user->conf->THEME_ELDY_USE_CHECKED === '1'?'e6edf0':$user->conf->THEME_ELDY_USE_CHECKED)); } -//$colortopbordertitle1=$colorbackhmenu1; // Set text color to black or white $colorbackhmenu1=join(',', colorStringToArray($colorbackhmenu1)); // Normalize value to 'x,y,z' diff --git a/htdocs/theme/eldy/theme_vars.inc.php b/htdocs/theme/eldy/theme_vars.inc.php index 69ced3490ae..e4718a25212 100644 --- a/htdocs/theme/eldy/theme_vars.inc.php +++ b/htdocs/theme/eldy/theme_vars.inc.php @@ -48,6 +48,7 @@ $colorbacklineimpair2='255,255,255'; // line impair $colorbacklinepair1='250,250,250'; // line pair $colorbacklinepair2='250,250,250'; // line pair $colorbacklinepairhover='230,237,244'; // line hover +$colorbacklinepairchecked='230,237,244'; // line checked $colorbacklinebreak='223,218,220'; // line break $colorbackbody='255,255,255'; $colortexttitlenotab='100,60,20'; diff --git a/htdocs/theme/md/img/object_bom.png b/htdocs/theme/md/img/object_bom.png new file mode 100644 index 00000000000..38b59646d71 Binary files /dev/null and b/htdocs/theme/md/img/object_bom.png differ diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index e5f0b54e6e7..2906da3729c 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -99,7 +99,7 @@ if (! isset($conf->global->THEME_ELDY_BACKBODY)) $conf->global->THEME_ELDY_BACKB if (! isset($conf->global->THEME_ELDY_TOPMENU_BACK1)) $conf->global->THEME_ELDY_TOPMENU_BACK1=$colorbackhmenu1; if (! isset($conf->global->THEME_ELDY_VERMENU_BACK1)) $conf->global->THEME_ELDY_VERMENU_BACK1=$colorbackvmenu1; if (! isset($conf->global->THEME_ELDY_BACKTITLE1)) $conf->global->THEME_ELDY_BACKTITLE1=$colorbacktitle1; -if (! isset($conf->global->THEME_ELDY_USE_HOVER)) $conf->global->THEME_ELDY_USE_HOVER==$colorbacklinepairhover; +if (! isset($conf->global->THEME_ELDY_USE_HOVER)) $conf->global->THEME_ELDY_USE_HOVER=$colorbacklinepairhover; if (! isset($conf->global->THEME_ELDY_USE_CHECKED)) $conf->global->THEME_ELDY_USE_CHECKED=$colorbacklinepairchecked; if (! isset($conf->global->THEME_ELDY_LINEBREAK)) $conf->global->THEME_ELDY_LINEBREAK=$colorbacklinebreak; if (! isset($conf->global->THEME_ELDY_TEXTTITLENOTAB)) $conf->global->THEME_ELDY_TEXTTITLENOTAB=$colortexttitlenotab; @@ -137,17 +137,16 @@ $fontsize =empty($user->conf->THEME_ELDY_ENABLE_PERSONALIZED)?(empty( $fontsizesmaller =empty($user->conf->THEME_ELDY_ENABLE_PERSONALIZED)?(empty($conf->global->THEME_ELDY_FONT_SIZE2) ?$fontsize:$conf->global->THEME_ELDY_FONT_SIZE2) :(empty($user->conf->THEME_ELDY_FONT_SIZE2)?$fontsize:$user->conf->THEME_ELDY_FONT_SIZE2); // Hover color -$colorbacklinepairhover=((! isset($conf->global->THEME_ELDY_USE_HOVER) || (string) $conf->global->THEME_ELDY_USE_HOVER === '0')?'':($conf->global->THEME_ELDY_USE_HOVER === '1'?'edf4fb':$conf->global->THEME_ELDY_USE_HOVER)); -$colorbacklinepairchecked=((! isset($conf->global->THEME_ELDY_USE_CHECKED) || (string) $conf->global->THEME_ELDY_USE_CHECKED === '0')?'':($conf->global->THEME_ELDY_USE_CHECKED === '1'?'edf4fb':$conf->global->THEME_ELDY_USE_CHECKED)); +$colorbacklinepairhover=((! isset($conf->global->THEME_ELDY_USE_HOVER) || (string) $conf->global->THEME_ELDY_USE_HOVER === '255,255,255')?'':($conf->global->THEME_ELDY_USE_HOVER === '1'?'edf4fb':$conf->global->THEME_ELDY_USE_HOVER)); +$colorbacklinepairchecked=((! isset($conf->global->THEME_ELDY_USE_CHECKED) || (string) $conf->global->THEME_ELDY_USE_CHECKED === '255,255,255')?'':($conf->global->THEME_ELDY_USE_CHECKED === '1'?'edf4fb':$conf->global->THEME_ELDY_USE_CHECKED)); if (! empty($user->conf->THEME_ELDY_ENABLE_PERSONALIZED)) { - $colorbacklinepairhover=((! isset($user->conf->THEME_ELDY_USE_HOVER) || $user->conf->THEME_ELDY_USE_HOVER === '0')?'':($user->conf->THEME_ELDY_USE_HOVER === '1'?'edf4fb':$user->conf->THEME_ELDY_USE_HOVER)); - $colorbacklinepairchecked=((! isset($user->conf->THEME_ELDY_USE_CHECKED) || $user->conf->THEME_ELDY_USE_CHECKED === '0')?'':($user->conf->THEME_ELDY_USE_CHECKED === '1'?'edf4fb':$user->conf->THEME_ELDY_USE_CHECKED)); + $colorbacklinepairhover=((! isset($user->conf->THEME_ELDY_USE_HOVER) || $user->conf->THEME_ELDY_USE_HOVER === '255,255,255')?'':($user->conf->THEME_ELDY_USE_HOVER === '1'?'edf4fb':$user->conf->THEME_ELDY_USE_HOVER)); + $colorbacklinepairchecked=((! isset($user->conf->THEME_ELDY_USE_CHECKED) || $user->conf->THEME_ELDY_USE_CHECKED === '255,255,255')?'':($user->conf->THEME_ELDY_USE_CHECKED === '1'?'edf4fb':$user->conf->THEME_ELDY_USE_CHECKED)); } if (empty($colortopbordertitle1)) $colortopbordertitle1=$colorbackhmenu1; - // Set text color to black or white $colorbackhmenu1=join(',', colorStringToArray($colorbackhmenu1)); // Normalize value to 'x,y,z' $tmppart=explode(',', $colorbackhmenu1); @@ -324,7 +323,9 @@ input, select { margin-bottom:1px; margin-top:1px; } - +input.button.massactionconfirmed { + margin: 4px; +} textarea { border-radius: 0; @@ -351,18 +352,25 @@ input.smallpadd { /* Used for timesheet input */ input.buttongen { vertical-align: middle; } -input.buttonpayment { +input.buttonpayment, button.buttonpayment, div.buttonpayment { min-width: 320px; margin-bottom: 15px; background-image: none; line-height: 24px; padding: 8px; background: none; - padding-left: 30px; - text-align: ; + text-align: center; border: 2px solid #ccc; background-color: #eee; white-space: normal; + color: #888 !important; +} +div.buttonpayment input { + background-color: unset; + border-bottom: unset; + font-weight: bold; + text-transform: uppercase; + color: #333; } input.buttonpaymentcb { background-image: url(); @@ -536,6 +544,10 @@ hr { border: 0; border-top: 1px solid #ccc; } box-shadow: none; -webkit-box-shadow: none; } +.button_search, .button_removefilter { + border: unset; + background: unset; +} form { padding:0px; margin:0px; @@ -654,6 +666,15 @@ textarea.centpercent { .cursornotallowed { cursor: not-allowed; } +.backgroundblank { + background-color: #fff; +} +.checkboxattachfilelabel { + font-size: 0.85em; + opacity: 0.7; +} + +/* Themes for badges */ .badge { display: inline-block; min-width: 10px; @@ -1528,6 +1549,10 @@ img.photorefnoborder { margin: 0; padding-bottom: 0 !important; } +/* Payment Screen : Pointer cursor in the autofill image */ +.AutoFillAmount { + cursor:pointer; +} /* ============================================================================== */ @@ -2149,7 +2174,7 @@ div.vmenu, td.vmenu { .companylogo { padding-top: 4px; } .searchform { padding-top: 10px; } -a.vmenu:link, a.vmenu:visited, a.vmenu:hover, a.vmenu:active, span.vmenu { white-space: nowrap; font-size:px; font-family: ; text-align: ; font-weight: bold; } +a.vmenu:link, a.vmenu:visited, a.vmenu:hover, a.vmenu:active, span.vmenu, span.vsmenu { white-space: nowrap; font-size:px; font-family: ; text-align: ; font-weight: bold; } font.vmenudisabled { font-size:px; font-family: ; text-align: ; font-weight: bold; color: #aaa; margin-left: 4px; } a.vmenu:link, a.vmenu:visited { color: #; } @@ -3006,13 +3031,14 @@ ul.noborder li:nth-child(odd):not(.liste_titre) { box-shadow: unset; -webkit-box-shadow: unset; } + .oddeven:hover, .evenodd:hover, .impair:hover, .pair:hover { - background: rgb() !important; - } - +.tredited { + background: rgb() !important; /* Must be background to be stronger than background of odd or even */ +} .highlight { background: rgb() !important; /* Must be background to be stronger than background of odd or even */ @@ -3181,8 +3207,7 @@ input.liste_titre { .noborder tr.liste_total td, tr.liste_total td, form.liste_total div, .noborder tr.liste_total_wrap td, tr.liste_total_wrap td, form.liste_total_wrap div { color: #332266; - font-weight: normal; - padding: 4px; + /* padding: 4px; */ } .noborder tr.liste_total td, tr.liste_total td, form.liste_total div { white-space: nowrap; @@ -3473,7 +3498,7 @@ img.boxhandle, img.boxclose { * Ok, Warning, Error */ .ok { color: #114466; } -.warning { color: #887711; } +.warning { color: #887711 !important; } .error { color: #550000 !important; font-weight: bold; } div.ok { @@ -3642,6 +3667,7 @@ div#card-errors { color: #fa755a; text-align: center; padding-top: 3px; + max-width: 320px; } diff --git a/htdocs/theme/md/theme_vars.inc.php b/htdocs/theme/md/theme_vars.inc.php index 47b8d93fdbb..95ac3abdd34 100644 --- a/htdocs/theme/md/theme_vars.inc.php +++ b/htdocs/theme/md/theme_vars.inc.php @@ -44,7 +44,8 @@ $colorbacklineimpair1='255,255,255'; // line impair $colorbacklineimpair2='255,255,255'; // line impair $colorbacklinepair1='248,248,248'; // line pair $colorbacklinepair2='246,246,246'; // line pair -$colorbacklinepairhover='230,237,244'; // line pair +$colorbacklinepairhover='230,237,244'; // line hover +$colorbacklinepairchecked='230,237,244'; // line checked $colorbacklinebreak='214,218,220'; $colorbackbody='248,248,248'; $colortexttitlenotab='90,90,90'; diff --git a/htdocs/ticket/agenda.php b/htdocs/ticket/agenda.php index eb863adbfbc..bad8e58497b 100644 --- a/htdocs/ticket/agenda.php +++ b/htdocs/ticket/agenda.php @@ -125,103 +125,102 @@ if (! empty($conf->global->MAIN_HTML_TITLE) && preg_match('/ticketnameonly/', $c $help_url = 'FR:DocumentationModuleTicket'; llxHeader('', $title, $help_url); - if ($socid > 0) { - $object->fetch_thirdparty(); - $head = societe_prepare_head($object->thirdparty); +if ($socid > 0) { + $object->fetch_thirdparty(); + $head = societe_prepare_head($object->thirdparty); - dol_fiche_head($head, 'ticket', $langs->trans("ThirdParty"), 0, 'company'); + dol_fiche_head($head, 'ticket', $langs->trans("ThirdParty"), 0, 'company'); - dol_banner_tab($object->thirdparty, 'socid', '', ($user->societe_id ? 0 : 1), 'rowid', 'nom'); + dol_banner_tab($object->thirdparty, 'socid', '', ($user->societe_id ? 0 : 1), 'rowid', 'nom'); - dol_fiche_end(); - } + dol_fiche_end(); +} - if (!$user->societe_id && $conf->global->TICKET_LIMIT_VIEW_ASSIGNED_ONLY) { - $object->next_prev_filter = "te.fk_user_assign = '" . $user->id . "'"; - } elseif ($user->societe_id > 0) { - $object->next_prev_filter = "te.fk_soc = '" . $user->societe_id . "'"; - } - $head = ticket_prepare_head($object); +if (!$user->societe_id && $conf->global->TICKET_LIMIT_VIEW_ASSIGNED_ONLY) { + $object->next_prev_filter = "te.fk_user_assign = '" . $user->id . "'"; +} elseif ($user->societe_id > 0) { + $object->next_prev_filter = "te.fk_soc = '" . $user->societe_id . "'"; +} +$head = ticket_prepare_head($object); - dol_fiche_head($head, 'tabTicketLogs', $langs->trans("Ticket"), 0, 'ticket'); +dol_fiche_head($head, 'tabTicketLogs', $langs->trans("Ticket"), 0, 'ticket'); - $morehtmlref ='
'; - $morehtmlref.= $object->subject; - // Author - if ($object->fk_user_create > 0) { - $morehtmlref .= '
' . $langs->trans("CreatedBy") . ' '; +$morehtmlref ='
'; +$morehtmlref.= $object->subject; +// Author +if ($object->fk_user_create > 0) { + $morehtmlref .= '
' . $langs->trans("CreatedBy") . ' : '; - $langs->load("users"); - $fuser = new User($db); - $fuser->fetch($object->fk_user_create); - $morehtmlref .= $fuser->getNomUrl(0); - } - if (!empty($object->origin_email)) { - $morehtmlref .= '
' . $langs->trans("CreatedBy") . ' : '; - $morehtmlref .= $object->origin_email . ' (' . $langs->trans("TicketEmailOriginIssuer") . ')'; - } + $langs->load("users"); + $fuser = new User($db); + $fuser->fetch($object->fk_user_create); + $morehtmlref .= $fuser->getNomUrl(0); +} +if (!empty($object->origin_email)) { + $morehtmlref .= '
' . $langs->trans("CreatedBy") . ' : '; + $morehtmlref .= $object->origin_email . ' (' . $langs->trans("TicketEmailOriginIssuer") . ')'; +} - // Thirdparty - if (! empty($conf->societe->enabled)) - { - $morehtmlref.='
'.$langs->trans('ThirdParty'); - /*if ($action != 'editcustomer' && $object->fk_statut < 8 && !$user->societe_id && $user->rights->ticket->write) { - $morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('Edit'), 1) . ''; - }*/ - $morehtmlref.=' : '; - if ($action == 'editcustomer') { - $morehtmlref.=$form->form_thirdparty($url_page_current . '?track_id=' . $object->track_id, $object->socid, 'editcustomer', '', 1, 0, 0, array(), 1); - } else { - $morehtmlref.=$form->form_thirdparty($url_page_current . '?track_id=' . $object->track_id, $object->socid, 'none', '', 1, 0, 0, array(), 1); - } - } +// Thirdparty +if (! empty($conf->societe->enabled)) +{ + $morehtmlref.='
'.$langs->trans('ThirdParty'); + /*if ($action != 'editcustomer' && $object->fk_statut < 8 && !$user->societe_id && $user->rights->ticket->write) { + $morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('Edit'), 1) . ''; + }*/ + $morehtmlref.=' : '; + if ($action == 'editcustomer') { + $morehtmlref.=$form->form_thirdparty($url_page_current . '?track_id=' . $object->track_id, $object->socid, 'editcustomer', '', 1, 0, 0, array(), 1); + } else { + $morehtmlref.=$form->form_thirdparty($url_page_current . '?track_id=' . $object->track_id, $object->socid, 'none', '', 1, 0, 0, array(), 1); + } +} - // Project - if (! empty($conf->projet->enabled)) - { - $langs->load("projects"); - $morehtmlref.='
'.$langs->trans('Project'); - if ($user->rights->ticket->write) - { - if ($action != 'classify') - //$morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('SetProject')) . ''; - $morehtmlref.=' : '; - if ($action == 'classify') { - //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); - $morehtmlref.='
'; - $morehtmlref.=''; - $morehtmlref.=''; - $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', 0, 0, 1, 0, 1, 0, 0, '', 1); - $morehtmlref.=''; - $morehtmlref.='
'; - } else { - $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); - } - } else { - if (! empty($object->fk_project)) { - $proj = new Project($db); - $proj->fetch($object->fk_project); - $morehtmlref.=$proj->getNomUrl(1); - } else { - $morehtmlref.=''; - } - } - } +// Project +if (! empty($conf->projet->enabled)) +{ + $langs->load("projects"); + $morehtmlref.='
'.$langs->trans('Project'); + if ($user->rights->ticket->write) + { + if ($action != 'classify') + //$morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('SetProject')) . ''; + $morehtmlref.=' : '; + if ($action == 'classify') { + //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); + $morehtmlref.='
'; + $morehtmlref.=''; + $morehtmlref.=''; + $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', 0, 0, 1, 0, 1, 0, 0, '', 1); + $morehtmlref.=''; + $morehtmlref.='
'; + } else { + $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); + } + } else { + if (! empty($object->fk_project)) { + $proj = new Project($db); + $proj->fetch($object->fk_project); + $morehtmlref.=$proj->getNomUrl(1); + } else { + $morehtmlref.=''; + } + } +} - $morehtmlref.='
'; +$morehtmlref.='
'; - $linkback = '' . $langs->trans("BackToList") . ' '; +$linkback = '' . $langs->trans("BackToList") . ' '; - dol_banner_tab($object, 'ref', $linkback, ($user->societe_id ? 0 : 1), 'ref', 'ref', $morehtmlref, '', 0, '', '', 1); +dol_banner_tab($object, 'ref', $linkback, ($user->societe_id ? 0 : 1), 'ref', 'ref', $morehtmlref, '', 0, '', '', 1); - dol_fiche_end(); +dol_fiche_end(); - print '
'; +print '
'; if (!empty($object->id)) { - print '
'; $param='&id='.$object->id; if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; diff --git a/htdocs/ticket/card.php b/htdocs/ticket/card.php index fd434ef395f..26fec804370 100644 --- a/htdocs/ticket/card.php +++ b/htdocs/ticket/card.php @@ -75,7 +75,7 @@ if (empty($action) && empty($id) && empty($ref)) $action='view'; //Select mail models is same action as add_message if (GETPOST('modelselected', 'alpha')) { - $action = 'add_message'; + $action = 'create_message'; } // Load object @@ -123,8 +123,427 @@ if ($cancel) $action='view'; } -// Do action -$actionobject->doActions($action, $object); +if (GETPOST('add', 'alpha') && $user->rights->ticket->write) { + $error = 0; + + if (!GETPOST("subject", 'alpha')) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject")), null, 'errors'); + $action = 'create'; + } elseif (!GETPOST("message", 'alpha')) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Message")), null, 'errors'); + $action = 'create'; + } + + if (!$error) { + $db->begin(); + + $object->ref = GETPOST("ref", 'alpha'); + $object->fk_soc = GETPOST("socid", 'int') > 0 ? GETPOST("socid", 'int') : 0; + $object->subject = GETPOST("subject", 'alpha'); + $object->message = GETPOST("message", 'none'); + + $object->type_code = GETPOST("type_code", 'alpha'); + $object->category_code = GETPOST("category_code", 'alpha'); + $object->severity_code = GETPOST("severity_code", 'alpha'); + $notifyTiers = GETPOST("notify_tiers_at_create", 'alpha'); + $object->notify_tiers_at_create = empty($notifyTiers) ? 0 : 1; + + $object->fk_project = GETPOST('projectid', 'int'); + + $extrafields = new ExtraFields($db); + $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object); + + $id = $object->create($user); + if ($id <= 0) { + $error++; + setEventMessage($object->error, $object->errors, 'errors'); + $action = 'create'; + } + + if (! $error) + { + // Add contact + $contactid = GETPOST('contactid', 'int'); + $type_contact = GETPOST("type", 'alpha'); + + if ($contactid > 0 && $type_contact) { + $result = $object->add_contact($contactid, GETPOST("type"), 'external'); + } + + // altairis: link ticket to project + if (GETPOST('projectid') > 0) { + $object->setProject(GETPOST('projectid')); + } + + // Auto assign user + if ($conf->global->TICKET_AUTO_ASSIGN_USER_CREATE) { + $result = $object->assignUser($user, $user->id, 1); + $object->add_contact($user->id, "SUPPORTTEC", 'internal'); + } + + // Auto assign contrat + $contractid = 0; + if ($conf->global->TICKET_AUTO_ASSIGN_CONTRACT_CREATE) { + $contrat = new Contrat($db); + $contrat->socid = $object->fk_soc; + $list = $contrat->getListOfContracts(); + + if (is_array($list) && !empty($list)) { + if (count($list) == 1) { + $contractid = $list[0]->id; + $object->setContract($contractid); + } else { + } + } + } + + // Auto create fiche intervention + if ($conf->global->TICKET_AUTO_CREATE_FICHINTER_CREATE) + { + $fichinter = new Fichinter($db); + $fichinter->socid = $object->fk_soc; + $fichinter->fk_project = GETPOST('projectid', 'int'); + $fichinter->fk_contrat = $contractid; + $fichinter->author = $user->id; + $fichinter->modelpdf = 'soleil'; + $fichinter->origin = $object->element; + $fichinter->origin_id = $object->id; + + // Extrafields + $extrafields = new ExtraFields($db); + $extralabels = $extrafields->fetch_name_optionals_label($fichinter->table_element); + $array_options = $extrafields->getOptionalsFromPost($fichinter->table_element); + $fichinter->array_options = $array_options; + + $id = $fichinter->create($user); + if ($id <= 0) { + setEventMessages($fichinter->error, null, 'errors'); + } + } + } + + if (! $error) + { + // File transfer + $object->copyFilesForTicket(); + } + + if (! $error) + { + $db->commit(); + + if (!empty($backtopage)) { + $url = $backtopage; + } else { + $url = 'card.php?track_id=' . $object->track_id; + } + + header("Location: " . $url); + exit; + } else { + $db->rollback(); + setEventMessages($object->error, $object->errors, 'errors'); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } +} + +if ($action == 'edit' && $user->rights->ticket->write) { + $error = 0; + + if ($object->fetch(GETPOST('id')) < 0) { + $error++; + array_push($object->errors, $langs->trans("ErrorTicketIsNotValid")); + $_GET["action"] = $_POST["action"] = ''; + } +} + +if (GETPOST('update') && GETPOST('id') && $user->rights->ticket->write) { + $error = 0; + + $ret = $object->fetch(GETPOST('id')); + if ($ret < 0) { + $error++; + array_push($object->errors, $langs->trans("ErrorTicketIsNotValid")); + $action = ''; + } elseif (!GETPOST("label")) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Label"))); + $action = 'edit'; + } elseif (!GETPOST("subject")) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject"))); + $action = 'edit'; + } + + if (!$error) { + $db->begin(); + + $object->label = GETPOST("label"); + $object->description = GETPOST("description"); + + //... + $ret = $object->update($user); + if ($ret <= 0) { + $error++; + setEventMessage($object->error, $object->errors, 'errors'); + $action = 'edit'; + } + + if (!$error && $ret > 0) { + $db->commit(); + } else { + $db->rollback(); + } + } +} + +if ($action == "mark_ticket_read" && $user->rights->ticket->write) { + $object->fetch('', '', GETPOST("track_id", 'alpha')); + + if ($object->markAsRead($user) > 0) + { + setEventMessages($langs->trans('TicketMarkedAsRead'), null, 'mesgs'); + + header("Location: card.php?track_id=" . $object->track_id . "&action=view"); + exit; + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + $action = 'view'; +} + +if ($action == "assign_user" && GETPOST('btn_assign_user', 'aplha') && $user->rights->ticket->write) { + $object->fetch('', '', GETPOST("track_id", 'alpha')); + $useroriginassign = $object->fk_user_assign; + $usertoassign = GETPOST('fk_user_assign', 'int'); + + /*if (! ($usertoassign > 0)) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("AssignedTo"))); + $action = 'view'; + }*/ + + if (!$error) + { + $ret = $object->assignUser($user, $usertoassign); + if ($ret < 0) $error++; + } + + if (! $error) // Update list of contacts + { + // Si déjà un user assigné on le supprime des contacts + if ($useroriginassign > 0) { + $internal_contacts = $object->listeContact(-1, 'internal'); + + foreach ($internal_contacts as $key => $contact) { + if ($contact['code'] == "SUPPORTTEC" && $contact['id'] == $useroriginassign) { + } + { + //print "user à effacer : ".$useroriginassign; + $object->delete_contact($contact['rowid']); + } + } + } + + if ($usertoassign > 0) $object->add_contact($usertoassign, "SUPPORTTEC", 'internal', $notrigger = 0); + } + + if (! $error) + { + // Log action in ticket logs table + $object->fetch_user($usertoassign); + $log_action = $langs->trans('TicketLogAssignedTo', $object->user->getFullName($langs)); + + setEventMessages($langs->trans('TicketAssigned'), null, 'mesgs'); + + header("Location: card.php?track_id=" . $object->track_id . "&action=view"); + exit; + } else { + array_push($object->errors, $object->error); + } + $action = 'view'; +} + +if ($action == "add_message" && GETPOST('btn_add_message') && $user->rights->ticket->read) { + $ret = $object->newMessage($user, $action, (GETPOST('private_message', 'alpha') == "on" ? 1 : 0)); + + if ($ret > 0) { + if (!empty($backtopage)) { + $url = $backtopage; + } else { + $url = 'card.php?action=view&track_id=' . $object->track_id; + } + + header("Location: " . $url); + exit; + } else { + setEventMessages($object->error, null, 'errors'); + $action = 'create_message'; + } +} + +if ($action == "confirm_close" && GETPOST('confirm', 'alpha') == 'yes' && $user->rights->ticket->write) +{ + $object->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')); + + if ($object->close($user)) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogClosedBy', $user->getFullName($langs)); + + setEventMessages($langs->trans('TicketMarkedAsClosed'), null, 'mesgs'); + + $url = 'card.php?action=view&track_id=' . GETPOST('track_id', 'alpha'); + header("Location: " . $url); + } else { + $action = ''; + setEventMessages($object->error, $object->errors, 'errors'); + } +} + +if ($action == "confirm_public_close" && GETPOST('confirm', 'alpha') == 'yes') { + $object->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')); + if (($_SESSION['email_customer'] == $object->origin_email || $_SESSION['email_customer'] == $object->thirdparty->email) && $object->close()) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogClosedBy', $_SESSION['email_customer']); + + setEventMessages('
' . $langs->trans('TicketMarkedAsClosed') . '
', null, 'mesgs'); + + $url = 'view.php?action=view_ticket&track_id=' . GETPOST('track_id', 'alpha'); + header("Location: " . $url); + } else { + setEventMessages($object->error, $object->errors, 'errors'); + $action = ''; + } +} + +if ($action == 'confirm_delete_ticket' && GETPOST('confirm', 'alpha') == "yes" && $user->rights->ticket->delete) { + if ($object->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + if ($object->delete($user) > 0) { + setEventMessages('
' . $langs->trans('TicketDeletedSuccess') . '
', null, 'mesgs'); + Header("Location: ".DOL_URL_ROOT."/ticket/list.php"); + exit; + } else { + $langs->load("errors"); + $mesg = '
' . $langs->trans($object->error) . '
'; + $action = ''; + } + } +} + +// Set parent company +if ($action == 'set_thirdparty' && $user->rights->societe->creer) { + if ($object->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + $result = $object->setCustomer(GETPOST('editcustomer', 'int')); + $url = 'card.php?action=view&track_id=' . GETPOST('track_id', 'alpha'); + header("Location: " . $url); + exit(); + } +} + +if ($action == 'set_progression' && $user->rights->ticket->write) { + if ($object->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + $result = $object->setProgression(GETPOST('progress', 'alpha')); + + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } +} + +if ($action == 'setsubject') { + if ($object->fetch(GETPOST('id', 'int'))) { + if ($action == 'setsubject') { + $object->subject = trim(GETPOST('subject', 'alpha')); + } + + if ($action == 'setsubject' && empty($object->subject)) { + $mesg .= ($mesg ? '
' : '') . $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject")); + } + + if (!$mesg) { + if ($object->update($user) >= 0) { + header("Location: " . $_SERVER['PHP_SELF'] . "?track_id=" . $object->track_id); + exit; + } + $mesg = $object->error; + } + } +} + + +if ($action == 'confirm_reopen' && $user->rights->ticket->manage && !GETPOST('cancel')) { + if ($object->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + // prevent browser refresh from reopening ticket several times + if ($object->fk_statut == Ticket::STATUS_CLOSED) { + $res = $object->setStatut(Ticket::STATUS_ASSIGNED); + if ($res) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogReopen'); + + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } + } + } +} // Categorisation dans projet +elseif ($action == 'classin' && $user->rights->ticket->write) { + if ($object->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + $object->setProject(GETPOST('projectid', 'int')); + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } +} // Categorisation dans contrat +elseif ($action == 'setcontract' && $user->rights->ticket->write) { + if ($object->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + $object->setContract(GETPOST('contractid', 'int')); + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } +} elseif ($action == "set_message" && $user->rights->ticket->manage) { + // altairis: manage cancel button + if (!GETPOST('cancel')) { + $object->fetch('', '', GETPOST('track_id', 'alpha')); + $oldvalue_message = $object->message; + $fieldtomodify = GETPOST('message_initial'); + + $object->message = $fieldtomodify; + $ret = $object->update($user); + if ($ret > 0) { + $log_action = $langs->trans('TicketInitialMessageModified') . " \n"; + // include the Diff class + dol_include_once('/ticket/class/utils_diff.class.php'); + // output the result of comparing two files as plain text + $log_action .= Diff::toString(Diff::compare(strip_tags($oldvalue_message), strip_tags($object->message))); + + setEventMessages($langs->trans('TicketMessageSuccesfullyUpdated'), null, 'mesgs'); + } + } + + $action = 'view'; +} // Reopen ticket +elseif ($action == 'confirm_set_status' && $user->rights->ticket->write && !GETPOST('cancel')) { + if ($object->fetch(GETPOST('id', 'int'), GETPOST('track_id', 'alpha')) >= 0) { + $new_status = GETPOST('new_status', 'int'); + $old_status = $object->fk_statut; + $res = $object->setStatut($new_status); + if ($res) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogStatusChanged', $langs->transnoentities($object->statuts_short[$old_status]), $langs->transnoentities($object->statuts_short[$new_status])); + + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } + } +} // Action to update one extrafield if ($action == "update_extras" && ! empty($permissiontoadd)) @@ -164,9 +583,21 @@ if ($action == "change_property" && GETPOST('btn_update_ticket_prop', 'alpha') & $action = 'view'; } + $permissiondellink = $user->rights->ticket->write; include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be include, not include_once +// Actions to build doc +$upload_dir = $conf->ticket->dir_output; +$permissioncreate = $user->rights->ticket->write; +include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; + +// Actions to send emails +$trigger_name='TICKET_SENTBYMAIL'; +$paramname='id'; +$autocopy='MAIN_MAIL_AUTOCOPY_TICKET_TO'; // used to know the automatic BCC to add +$trackid='tic'.$object->id; +include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php'; @@ -184,7 +615,7 @@ $page_title = $actionobject->getTitle($action); llxHeader('', $page_title, $help_url); -if ($action == 'create') +if ($action == 'create' || $action == 'presend') { $formticket = new FormTicket($db); @@ -207,10 +638,9 @@ if ($action == 'create') $formticket->showForm(1); } -if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'dellink' || $action == 'add_message' || $action == 'close' || $action == 'delete' || $action == 'editcustomer' || $action == 'progression' || $action == 'reopen' +if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'dellink' || $action == 'create_message' || $action == 'close' || $action == 'delete' || $action == 'editcustomer' || $action == 'progression' || $action == 'reopen' || $action == 'editsubject' || $action == 'edit_extras' || $action == 'update_extras' || $action == 'edit_extrafields' || $action == 'set_extrafields' || $action == 'classify' || $action == 'sel_contract' || $action == 'edit_message_init' || $action == 'set_status' || $action == 'dellink') { - if ($res > 0) { // or for unauthorized internals users @@ -388,7 +818,7 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd $morehtmlref.='
'; - $linkback = '' . $langs->trans("BackToList") . ' '; + $linkback = '' . $langs->trans("BackToList") . ' '; dol_banner_tab($object, 'ref', $linkback, ($user->societe_id ? 0 : 1), 'ref', 'ref', $morehtmlref); @@ -525,12 +955,7 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd $actionobject->viewTicketOriginalMessage($user, $action, $object); - /*************************************************** - * - * Classification and actions on ticket - * - ***************************************************/ - + // Classification of ticket print '
'; print ''; print ''; @@ -557,19 +982,14 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd print ''; if (GETPOST('set', 'alpha') == 'properties' && $user->rights->ticket->write) { print ''; + // Type print ''; print $langs->trans('TicketChangeType'); print ''; print $formticket->selectTypesTickets($object->type_code, 'update_value_type', '', 2); print ''; print ''; - print ''; - print ''; - print $langs->trans('TicketChangeSeverity'); - print ''; - print $formticket->selectSeveritiesTickets($object->severity_code, 'update_value_severity', '', 2); - print ''; - print ''; + // Group print ''; print ''; print $langs->trans('TicketChangeCategory'); @@ -577,6 +997,14 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd print $formticket->selectGroupTickets($object->category_code, 'update_value_category', '', 2); print ''; print ''; + // Severity + print ''; + print ''; + print $langs->trans('TicketChangeSeverity'); + print ''; + print $formticket->selectSeveritiesTickets($object->severity_code, 'update_value_severity', '', 2); + print ''; + print ''; } else { // Type print '' . $langs->trans("Type") . ''; @@ -585,15 +1013,6 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); }*/ print ''; - - // Severity - print '' . $langs->trans("TicketSeverity") . ''; - print $langs->getLabelFromKey($db, $object->severity_code, 'c_ticket_severity', 'code', 'label'); - /*if ($user->admin && !$noadmininfo) { - print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - }*/ - print ''; - // Group print '' . $langs->trans("TicketGroup") . ''; print $langs->getLabelFromKey($db, $object->category_code, 'c_ticket_category', 'code', 'label'); @@ -601,6 +1020,13 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); }*/ print ''; + // Severity + print '' . $langs->trans("TicketSeverity") . ''; + print $langs->getLabelFromKey($db, $object->severity_code, 'c_ticket_severity', 'code', 'label'); + /*if ($user->admin && !$noadmininfo) { + print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + }*/ + print ''; } print ''; // End table actions @@ -744,8 +1170,8 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd if (empty($reshook)) { // Show link to add a message (if read and not closed) - if ($object->fk_statut < Ticket::STATUS_CLOSED && $action != "add_message") { - print ''; + if ($object->fk_statut < Ticket::STATUS_CLOSED && $action != "create_message") { + print ''; } // Link to create an intervention @@ -781,7 +1207,7 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd $action = 'presend'; } - if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'dellink' || $action == 'delete' || $action == 'edit_message_init') + if ($action != 'create_message') { print '
'; print ''; // ancre @@ -792,22 +1218,54 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd print '
'; - // Message list - print load_fiche_titre($langs->trans('TicketMessagesList'), '', 'messages@ticket'); - $show_private_message = ($user->societe_id ? 0 : 1); - $actionobject->viewTicketTimelineMessages($show_private_message, true, $object); + // List of actions on element + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php'; + $formactions = new FormActions($db); + $somethingshown = $formactions->showactions($object, 'ticket', $socid, 1); print '
'; print '
'; print '
'; } - elseif ($action == 'add_message') + else { - $action='new_message'; + $action='add_message'; // action to use to post the message $modelmail='ticket_send'; + // Substitution array + $morehtmlright=''; + $help=""; + $substitutionarray=array(); + if ($object->fk_soc > 0) { + $object->fetch_thirdparty(); + $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name; + } + $substitutionarray['__SIGNATURE__'] = $user->signature; + $substitutionarray['__TICKETSUP_TRACKID__'] = $object->track_id; + $substitutionarray['__TICKETSUP_REF__'] = $object->ref; + $substitutionarray['__TICKETSUP_SUBJECT__'] = $object->subject; + $substitutionarray['__TICKETSUP_TYPE__'] = $object->type_code; + $substitutionarray['__TICKETSUP_SEVERITY__'] = $object->severity_code; + $substitutionarray['__TICKETSUP_CATEGORY__'] = $object->category_code; // For backward compatibility + $substitutionarray['__TICKETSUP_ANALYTIC_CODE__'] = $object->category_code; + $substitutionarray['__TICKETSUP_MESSAGE__'] = $object->message; + $substitutionarray['__TICKETSUP_PROGRESSION__'] = $object->progress; + if ($object->fk_user_assign > 0) { + $userstat->fetch($object->fk_user_assign); + $substitutionarray['__TICKETSUP_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname); + } + + if ($object->fk_user_create > 0) { + $userstat->fetch($object->fk_user_create); + $substitutionarray['__TICKETSUP_USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname); + } + foreach ($substitutionarray as $key => $val) { + $help.=$key.' -> '.$langs->trans($val).'
'; + } + $morehtmlright.=$form->textwithpicto($langs->trans("TicketMessageSubstitutionReplacedByGenericValues"), $help); + print '
'; - print load_fiche_titre($langs->trans('TicketAddMessage'), '', 'messages@ticket'); + print load_fiche_titre($langs->trans('TicketAddMessage'), $morehtmlright, 'messages@ticket'); print '
'; @@ -840,30 +1298,7 @@ if (empty($action) || $action == 'view' || $action == 'addlink' || $action == 'd $formticket->withsubstit = 1; - - if ($object->fk_soc > 0) { - $object->fetch_thirdparty(); - $formticket->substit['__THIRDPARTY_NAME__'] = $object->thirdparty->name; - } - $formticket->substit['__SIGNATURE__'] = $user->signature; - $formticket->substit['__TICKETSUP_TRACKID__'] = $object->track_id; - $formticket->substit['__TICKETSUP_REF__'] = $object->ref; - $formticket->substit['__TICKETSUP_SUBJECT__'] = $object->subject; - $formticket->substit['__TICKETSUP_TYPE__'] = $object->type_code; - $formticket->substit['__TICKETSUP_SEVERITY__'] = $object->severity_code; - $formticket->substit['__TICKETSUP_CATEGORY__'] = $object->category_code; // For backward compatibility - $formticket->substit['__TICKETSUP_ANALYTIC_CODE__'] = $object->category_code; - $formticket->substit['__TICKETSUP_MESSAGE__'] = $object->message; - $formticket->substit['__TICKETSUP_PROGRESSION__'] = $object->progress; - if ($object->fk_user_assign > 0) { - $userstat->fetch($object->fk_user_assign); - $formticket->substit['__TICKETSUP_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname); - } - - if ($object->fk_user_create > 0) { - $userstat->fetch($object->fk_user_create); - $formticket->substit['__TICKETSUP_USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname); - } + $formticket->substit = $substitutionarray; $formticket->showMessageForm('100%'); print '
'; diff --git a/htdocs/ticket/class/actions_ticket.class.php b/htdocs/ticket/class/actions_ticket.class.php index f978642311c..b10f8ebe7da 100644 --- a/htdocs/ticket/class/actions_ticket.class.php +++ b/htdocs/ticket/class/actions_ticket.class.php @@ -102,849 +102,6 @@ class ActionsTicket } } - /** - * doActions - * - * @param string $action Action type - * @param Ticket $object Object Ticket - * @return int 0 - */ - public function doActions(&$action = '', Ticket $object = null) - { - global $conf, $user, $langs, $mysoc; - - /* - * Add file in email form - */ - if (GETPOST('addfile', 'alpha')) { - // altairis : allow files from public interface - if (GETPOST('track_id', 'alpha')) { - $res = $object->fetch('', '', GETPOST('track_id', 'alpha')); - } - - ////if($res > 0) - ////{ - include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; - - // Set tmp directory TODO Use a dedicated directory for temp mails files - $vardir = $conf->ticket->dir_output . (!empty($object->track_id) ? '/' . dol_sanitizeFileName($object->track_id) : ''); - $upload_dir_tmp = $vardir . '/temp'; - if (!dol_is_dir($upload_dir_tmp)) { - dol_mkdir($upload_dir_tmp); - } - dol_add_file_process($upload_dir_tmp, 0, 0, 'addedfile', dol_print_date(dol_now(), '%Y%m%d%H%M%S') . '-__file__'); - $action = !empty($object->track_id) ? 'add_message' : 'create'; - ////} - } - - /* - * Remove file in email form - */ - if (GETPOST('removedfile', 'alpha')) { - // altairis : allow files from public interface - if (GETPOST('track_id')) { - $res = $object->fetch('', '', GETPOST('track_id', 'alpha')); - } - - ////if($res > 0) - ////{ - include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; - - // Set tmp directory - $vardir = $conf->ticket->dir_output . (!empty($object->track_id) ? '/' . dol_sanitizeFileName($object->track_id) : ''); - $upload_dir_tmp = $vardir . '/temp'; - - // TODO Delete only files that was uploaded from email form - dol_remove_file_process($_POST['removedfile'], 0); - $action = !empty($object->track_id) ? 'add_message' : 'create'; - ////} - } - - if (GETPOST('add', 'alpha') && $user->rights->ticket->write) { - $error = 0; - - if (!GETPOST("subject")) { - $error++; - $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject")); - $action = 'create'; - } elseif (!GETPOST("message")) { - $error++; - $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentities("message")); - $action = 'create'; - } - - if (!$error) { - $this->db->begin(); - - $object->track_id = generate_random_id(16); - - $object->ref = GETPOST("ref", 'alpha'); - $object->fk_soc = GETPOST("socid", 'int') > 0 ? GETPOST("socid", 'int') : 0; - $object->subject = GETPOST("subject", 'alpha'); - $object->message = GETPOST("message"); - - $object->type_code = GETPOST("type_code", 'alpha'); - $object->category_code = GETPOST("category_code", 'alpha'); - $object->severity_code = GETPOST("severity_code", 'alpha'); - $notifyTiers = GETPOST("notify_tiers_at_create", 'alpha'); - $object->notify_tiers_at_create = empty($notifyTiers) ? 0 : 1; - - $object->fk_project = GETPOST('projectid', 'int'); - - $extrafields = new ExtraFields($this->db); - $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object); - - $id = $object->create($user); - if ($id <= 0) { - $error++; - $this->error = $object->error; - $this->errors = $object->errors; - $action = 'create'; - } - - if (!$error && $id > 0) - { - $this->db->commit(); - - // File transfer - $this->copyFilesForTicket(); - - // Add contact - $contactid = GETPOST('contactid', 'int'); - $type_contact = GETPOST("type", 'alpha'); - - if ($contactid > 0 && $type_contact) { - $result = $object->add_contact($contactid, GETPOST("type"), 'external'); - } - - // altairis: link ticket to project - if (GETPOST('projectid') > 0) { - $object->setProject(GETPOST('projectid')); - } - - // Auto assign user - if ($conf->global->TICKET_AUTO_ASSIGN_USER_CREATE) { - $result = $object->assignUser($user, $user->id, 1); - $object->add_contact($user->id, "SUPPORTTEC", 'internal'); - } - - // Auto assign contrat - $contractid = 0; - if ($conf->global->TICKET_AUTO_ASSIGN_CONTRACT_CREATE) { - $contrat = new Contrat($this->db); - $contrat->socid = $object->fk_soc; - $list = $contrat->getListOfContracts(); - - if (is_array($list) && !empty($list)) { - if (count($list) == 1) { - $contractid = $list[0]->id; - $object->setContract($contractid); - } else { - } - } - } - - // Auto create fiche intervention - if ($conf->global->TICKET_AUTO_CREATE_FICHINTER_CREATE) - { - $fichinter = new Fichinter($this->db); - $fichinter->socid = $object->fk_soc; - $fichinter->fk_project = GETPOST('projectid', 'int'); - $fichinter->fk_contrat = $contractid; - $fichinter->author = $user->id; - $fichinter->modelpdf = 'soleil'; - $fichinter->origin = $object->element; - $fichinter->origin_id = $object->id; - - // Extrafields - $extrafields = new ExtraFields($this->db); - $extralabels = $extrafields->fetch_name_optionals_label($fichinter->table_element); - $array_options = $extrafields->getOptionalsFromPost($fichinter->table_element); - $fichinter->array_options = $array_options; - - $id = $fichinter->create($user); - if ($id <= 0) { - setEventMessages($fichinter->error, null, 'errors'); - } - } - - if (!empty($backtopage)) { - $url = $backtopage; - } else { - $url = 'card.php?track_id=' . $object->track_id; - } - - header("Location: " . $url); - exit; - } else { - $this->db->rollback(); - setEventMessages($this->error, $this->errors, 'errors'); - } - } else { - setEventMessages($this->error, $this->errors, 'errors'); - } - } - - if ($action == 'edit' && $user->rights->ticket->write) { - $error = 0; - - if ($object->fetch(GETPOST('id')) < 0) { - $error++; - array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); - $_GET["action"] = $_POST["action"] = ''; - } - } - - if (GETPOST('update') && GETPOST('id') && $user->rights->ticket->write) { - $error = 0; - - $ret = $object->fetch(GETPOST('id')); - if ($ret < 0) { - $error++; - array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); - $action = ''; - } elseif (!GETPOST("label")) { - $error++; - array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Label"))); - $action = 'edit'; - } elseif (!GETPOST("subject")) { - $error++; - array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject"))); - $action = 'edit'; - } - - if (!$error) { - $this->db->begin(); - - $object->label = GETPOST("label"); - $object->description = GETPOST("description"); - - //... - $ret = $object->update($user); - if ($ret <= 0) { - $error++; - $this->errors = $object->error; - $this->errors = $object->errors; - $action = 'edit'; - } - - if (!$error && $ret > 0) { - $this->db->commit(); - } else { - $this->db->rollback(); - } - } - } - - if ($action == "mark_ticket_read" && $user->rights->ticket->write) { - $object->fetch('', '', GETPOST("track_id", 'alpha')); - - if ($object->markAsRead($user) > 0) - { - setEventMessages($langs->trans('TicketMarkedAsRead'), null, 'mesgs'); - - header("Location: card.php?track_id=" . $object->track_id . "&action=view"); - exit; - } else { - $this->errors = $object->error; - $this->error = $object->error; - } - $action = 'view'; - } - - if ($action == "assign_user" && GETPOST('btn_assign_user', 'aplha') && $user->rights->ticket->write) { - $object->fetch('', '', GETPOST("track_id", 'alpha')); - $useroriginassign = $object->fk_user_assign; - $usertoassign = GETPOST('fk_user_assign', 'int'); - - /*if (! ($usertoassign > 0)) { - $error++; - array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("AssignedTo"))); - $action = 'view'; - }*/ - - if (!$error) - { - $ret = $object->assignUser($user, $usertoassign); - if ($ret < 0) $error++; - } - - if (! $error) // Update list of contacts - { - // Si déjà un user assigné on le supprime des contacts - if ($useroriginassign > 0) { - $internal_contacts = $object->listeContact(-1, 'internal'); - - foreach ($internal_contacts as $key => $contact) { - if ($contact['code'] == "SUPPORTTEC" && $contact['id'] == $useroriginassign) { - } - { - //print "user à effacer : ".$useroriginassign; - $object->delete_contact($contact['rowid']); - } - } - } - - if ($usertoassign > 0) $object->add_contact($usertoassign, "SUPPORTTEC", 'internal', $notrigger = 0); - } - - if (! $error) - { - // Log action in ticket logs table - $object->fetch_user($usertoassign); - $log_action = $langs->trans('TicketLogAssignedTo', $object->user->getFullName($langs)); - - setEventMessages($langs->trans('TicketAssigned'), null, 'mesgs'); - - header("Location: card.php?track_id=" . $object->track_id . "&action=view"); - exit; - } else { - array_push($this->errors, $object->error); - } - $action = 'view'; - } - - if ($action == "new_message" && GETPOST('btn_add_message') && $user->rights->ticket->read) { - $ret = $this->newMessage($user, $action); - if ($ret) { - if (!empty($backtopage)) { - $url = $backtopage; - } else { - $url = 'card.php?action=view&track_id=' . $object->track_id; - } - - header("Location: " . $url); - exit; - } else { - setEventMessages($object->error, null, 'errors'); - $action = 'add_message'; - } - } - - if ($action == "new_public_message" && GETPOST('btn_add_message')) { - $this->newMessagePublic($user, $action); - } - - if ($action == "confirm_close" && GETPOST('confirm', 'alpha') == 'yes' && $user->rights->ticket->write) { - $this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')); - if ($object->close()) { - // Log action in ticket logs table - $log_action = $langs->trans('TicketLogClosedBy', $user->getFullName($langs)); - - setEventMessages($langs->trans('TicketMarkedAsClosed'), null, 'mesgs'); - - $url = 'card.php?action=view&track_id=' . GETPOST('track_id', 'alpha'); - header("Location: " . $url); - } else { - $action = ''; - setEventMessages($this->error, $this->errors, 'errors'); - } - } - - if ($action == "confirm_public_close" && GETPOST('confirm', 'alpha') == 'yes') { - $this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')); - if (($_SESSION['email_customer'] == $object->origin_email || $_SESSION['email_customer'] == $object->thirdparty->email) && $object->close()) { - // Log action in ticket logs table - $log_action = $langs->trans('TicketLogClosedBy', $_SESSION['email_customer']); - - setEventMessages('
' . $langs->trans('TicketMarkedAsClosed') . '
', null, 'mesgs'); - - $url = 'view.php?action=view_ticket&track_id=' . GETPOST('track_id', 'alpha'); - header("Location: " . $url); - } else { - setEventMessages($this->error, $this->errors, 'errors'); - $action = ''; - } - } - - if ($action == 'confirm_delete_ticket' && GETPOST('confirm', 'alpha') == "yes" && $user->rights->ticket->delete) { - if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { - if ($object->delete($user) > 0) { - setEventMessages('
' . $langs->trans('TicketDeletedSuccess') . '
', null, 'mesgs'); - Header("Location: ".DOL_URL_ROOT."/ticket/list.php"); - exit; - } else { - $langs->load("errors"); - $mesg = '
' . $langs->trans($this->error) . '
'; - $action = ''; - } - } - } - - // Set parent company - if ($action == 'set_thirdparty' && $user->rights->societe->creer) { - if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { - $result = $object->setCustomer(GETPOST('editcustomer', 'int')); - $url = 'card.php?action=view&track_id=' . GETPOST('track_id', 'alpha'); - header("Location: " . $url); - exit(); - } - } - - if ($action == 'set_progression' && $user->rights->ticket->write) { - if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { - $result = $object->setProgression(GETPOST('progress', 'alpha')); - - $url = 'card.php?action=view&track_id=' . $object->track_id; - header("Location: " . $url); - exit(); - } - } - - if ($action == 'setsubject') { - if ($this->fetch(GETPOST('id', 'int'))) { - if ($action == 'setsubject') { - $object->subject = trim(GETPOST('subject', 'alpha')); - } - - if ($action == 'setsubject' && empty($object->subject)) { - $mesg .= ($mesg ? '
' : '') . $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject")); - } - - if (!$mesg) { - if ($object->update($user) >= 0) { - header("Location: " . $_SERVER['PHP_SELF'] . "?track_id=" . $object->track_id); - exit; - } - $mesg = $object->error; - } - } - } - - - if ($action == 'confirm_reopen' && $user->rights->ticket->manage && !GETPOST('cancel')) { - if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { - // prevent browser refresh from reopening ticket several times - if ($object->fk_statut == Ticket::STATUS_CLOSED) { - $res = $object->setStatut(Ticket::STATUS_ASSIGNED); - if ($res) { - // Log action in ticket logs table - $log_action = $langs->trans('TicketLogReopen'); - - $url = 'card.php?action=view&track_id=' . $object->track_id; - header("Location: " . $url); - exit(); - } - } - } - } // Categorisation dans projet - elseif ($action == 'classin' && $user->rights->ticket->write) { - if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { - $object->setProject(GETPOST('projectid', 'int')); - $url = 'card.php?action=view&track_id=' . $object->track_id; - header("Location: " . $url); - exit(); - } - } // Categorisation dans contrat - elseif ($action == 'setcontract' && $user->rights->ticket->write) { - if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { - $object->setContract(GETPOST('contractid', 'int')); - $url = 'card.php?action=view&track_id=' . $object->track_id; - header("Location: " . $url); - exit(); - } - } elseif ($action == "set_message" && $user->rights->ticket->manage) { - // altairis: manage cancel button - if (!GETPOST('cancel')) { - $this->fetch('', '', GETPOST('track_id', 'alpha')); - $oldvalue_message = $object->message; - $fieldtomodify = GETPOST('message_initial'); - - $object->message = $fieldtomodify; - $ret = $object->update($user); - if ($ret > 0) { - $log_action = $langs->trans('TicketInitialMessageModified') . " \n"; - // include the Diff class - dol_include_once('/ticket/class/utils_diff.class.php'); - // output the result of comparing two files as plain text - $log_action .= Diff::toString(Diff::compare(strip_tags($oldvalue_message), strip_tags($object->message))); - - setEventMessages($langs->trans('TicketMessageSuccesfullyUpdated'), null, 'mesgs'); - } - } - - $action = 'view'; - } // Reopen ticket - elseif ($action == 'confirm_set_status' && $user->rights->ticket->write && !GETPOST('cancel')) { - if ($this->fetch(GETPOST('id', 'int'), GETPOST('track_id', 'alpha')) >= 0) { - $new_status = GETPOST('new_status', 'int'); - $old_status = $object->fk_statut; - $res = $object->setStatut($new_status); - if ($res) { - // Log action in ticket logs table - $log_action = $langs->trans('TicketLogStatusChanged', $langs->transnoentities($object->statuts_short[$old_status]), $langs->transnoentities($object->statuts_short[$new_status])); - - $url = 'card.php?action=view&track_id=' . $object->track_id; - header("Location: " . $url); - exit(); - } - } - } - - return 0; - } - - /** - * Add new message on a ticket (private area) - * - * @param User $user User for action - * @param string $action Action string - * @return int - */ - private function newMessage($user, &$action) - { - global $mysoc, $conf, $langs; - - if (!class_exists('Contact')) { - include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; - } - - $contactstatic = new Contact($this->db); - - $error = 0; - - $object = new Ticket($this->db); - $ret = $object->fetch('', '', GETPOST('track_id', 'alpha')); - $object->socid = $object->fk_soc; - $object->fetch_thirdparty(); - if ($ret < 0) { - $error++; - array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); - $action = ''; - } - - if (!GETPOST("message")) { - $error++; - array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message"))); - $action = 'add_message'; - } - - if (!$error) { - $object->message = GETPOST("message"); - $object->private = GETPOST("private_message"); - $send_email = GETPOST('send_email', 'int'); - - $id = $object->createTicketMessage($user); - if ($id <= 0) { - $error++; - $this->errors = $object->error; - $this->errors = $object->errors; - $action = 'add_message'; - } - - if (!$error && $id > 0) { - setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs'); - - /* - * Send email to linked contacts - */ - if ($send_email > 0) { - // Retrieve internal contact datas - $internal_contacts = $object->getInfosTicketInternalContact(); - $sendto = array(); - if (is_array($internal_contacts) && count($internal_contacts) > 0) { - // altairis: set default subject - $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE; - $subject = GETPOST('subject') ? GETPOST('subject') : '[' . $label_title . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); - - $message_intro = $langs->trans('TicketNotificationEmailBody', "#" . $object->id); - $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE; - - $message = $langs->trans('TicketMessageMailIntroText'); - $message .= "\n\n"; - $message .= GETPOST('message'); - - // Coordonnées client - $message .= "\n\n"; - $message .= "==============================================\n"; - $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty') . " : " . $object->thirdparty->name : ''; - $message .= !empty($object->thirdparty->town) ? "\n" . $langs->trans('Town') . " : " . $object->thirdparty->town : ''; - $message .= !empty($object->thirdparty->phone) ? "\n" . $langs->trans('Phone') . " : " . $object->thirdparty->phone : ''; - - // Build array to display recipient list - foreach ($internal_contacts as $key => $info_sendto) { - // altairis: avoid duplicate notifications - if ($info_sendto['id'] == $user->id) { - continue; - } - - if ($info_sendto['email'] != '') { - if(!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; - - //Contact type - $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; - $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); - } - } - $message .= "\n"; - // URL ticket - $url_internal_ticket = dol_buildpath('/ticket/card.php', 2) . '?track_id=' . $object->track_id; - - // altairis: make html link on url - $message .= "\n" . $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal') . ' : ' . '' . $object->track_id . '' . "\n"; - - // Add global email address recipient - // altairis: use new TICKET_NOTIFICATION_EMAIL_TO configuration variable - if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) { - if(!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO; - } - - // altairis: dont try to send email if no recipient - if (!empty($sendto)) { - $this->sendTicketMessageByEmail($subject, $message, '', $sendto); - } - } - - /* - * Email for externals users if not private - */ - if (empty($object->private)) { - // Retrieve email of all contacts (external) - $external_contacts = $object->getInfosTicketExternalContact(); - - // If no contact, get email from thirdparty - if (is_array($external_contacts) && count($external_contacts) === 0) { - if (!empty($object->fk_soc)) { - $object->fetch_thirdparty($object->fk_soc); - $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id)); - $external_contacts = array_merge($external_contacts, $array_company); - } elseif (empty($object->fk_soc) && !empty($object->origin_email)) { - $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id)); - $external_contacts = array_merge($external_contacts, $array_external); - } - } - - $sendto = array(); - if (is_array($external_contacts) && count($external_contacts) > 0) { - // altairis: get default subject for email to external contacts - $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE; - $subject = GETPOST('subject') ? GETPOST('subject') : '[' . $label_title . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); - - $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro') : $conf->global->TICKET_MESSAGE_MAIL_INTRO; - $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE; - - // We put intro after - $message = GETPOST('message'); - $message .= "\n\n"; - - foreach ($external_contacts as $key => $info_sendto) { - // altairis: avoid duplicate emails to external contacts - if ($info_sendto['id'] == $user->contactid) { - continue; - } - - if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) { - if(!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; - - $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; - $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); - } - } - - // If public interface is not enable, use link to internal page into mail - $url_public_ticket = (!empty($conf->global->TICKET_ENABLE_PUBLIC_INTERFACE) ? - (!empty($conf->global->TICKET_URL_PUBLIC_INTERFACE) ? - $conf->global->TICKET_URL_PUBLIC_INTERFACE . '/view.php' : - dol_buildpath('/public/ticket/view.php', 2) - ) : - dol_buildpath('/ticket/card.php', 2) - ) . '?track_id=' . $object->track_id; - $message .= "\n" . $langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : ' . '' . $object->track_id . '' . "\n"; - - // Build final message - $message = $message_intro . $message; - - // Add signature - $message .= '
' . $message_signature; - - if (!empty($object->origin_email)) { - $sendto[] = $object->origin_email; - } - - if ($object->fk_soc > 0 && ! in_array($object->origin_email, $sendto)) { - $object->socid = $object->fk_soc; - $object->fetch_thirdparty(); - if(!empty($object->thirdparty->email)) $sendto[] = $object->thirdparty->email; - } - - // altairis: Add global email address reciepient - if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) { - if(!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO; - } - - // altairis: dont try to send email when no recipient - if (!empty($sendto)) { - $this->sendTicketMessageByEmail($subject, $message, '', $sendto); - } - } - } - } - - $this->copyFilesForTicket(); - - // Set status to "answered" if not set yet, only for internal users - if ($object->fk_statut < 3 && !$user->societe_id) { - $object->setStatut(3); - } - - return 1; - } else { - setEventMessages($object->error, $object->errors, 'errors'); - return -1; - } - } else { - setEventMessages($this->error, $this->errors, 'errors'); - return -1; - } - } - - /** - * Add new message on a ticket (public area) - * - * @param User $user User for action - * @param string $action Action string - * @return void - */ - private function newMessagePublic($user, &$action) - { - - global $mysoc, $conf, $langs; - - $object = new Ticket($this->db); - $error = 0; - $ret = $object->fetch('', '', GETPOST('track_id', 'alpha')); - $object->socid = $object->fk_soc; - $object->fetch_thirdparty(); - if ($ret < 0) { - $error++; - array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); - $action = ''; - } - - if (!GETPOST("message")) { - $error++; - array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message"))); - $action = 'add_message'; - } - - if (!$error) { - $object->message = (string) GETPOST("message"); - $id = $object->createTicketMessage($user); - if ($id <= 0) { - $error++; - $this->error = $object->error; - $this->errors = $object->errors; - $action = 'add_message'; - } - - if (!$error && $id > 0) { - setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs'); - - // Retrieve internal contact datas - $internal_contacts = $object->getInfosTicketInternalContact(); - $sendto = array(); - if (is_array($internal_contacts) && count($internal_contacts) > 0) { - $subject = '[' . $mysoc->name . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); - - $message = $langs->trans('TicketMessageMailIntroAutoNewPublicMessage', $object->subject); - $message .= "\n"; - $message .= GETPOST('message'); - $message .= "\n"; - - // Coordonnées client - if ($object->thirdparty->id > 0) { - $message .= "\n\n"; - $message .= "==============================================\n"; - $message .= $langs->trans('Thirparty') . " : " . $object->thirdparty->name; - $message .= !empty($object->thirdparty->town) ? $langs->trans('Town') . " : " . $object->thirdparty->town : ''; - $message .= "\n"; - $message .= !empty($object->thirdparty->phone) ? $langs->trans('Phone') . " : " . $object->thirdparty->phone : ''; - $message .= "\n"; - } - - // Build array to display recipient list - foreach ($internal_contacts as $key => $info_sendto) { - if ($info_sendto['email'] != '') { - $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; - } - - // Contact type - $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; - $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); - $message .= "\n"; - } - - // URL ticket - $url_internal_ticket = dol_buildpath('/ticket/card.php', 2) . '?track_id=' . $object->track_id; - $message .= "\n" . $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal') . ' : ' . $url_internal_ticket . "\n"; - - $message .= "\n\n"; - - $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE; - - // Add global email address reciepient - if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_FROM, $sendto)) { - $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM; - } - - $this->sendTicketMessageByEmail($subject, $message, '', $sendto); - } - - /* - * Email for externals users if not private - */ - - // Retrieve email of all contacts external - $external_contacts = $object->getInfosTicketExternalContact(); - $sendto = array(); - if (is_array($external_contacts) && count($external_contacts) > 0) { - $subject = '[' . $mysoc->name . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); - - $message = $langs->trans('TicketMessageMailIntroAutoNewPublicMessage', $object->subject); - $message .= "\n"; - - $message .= GETPOST('message'); - $message .= "\n\n"; - - $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE; - foreach ($external_contacts as $key => $info_sendto) { - if ($info_sendto['email'] != '') { - $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; - } - $recipient = ''; - $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; - $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); - } - - $url_public_ticket = ($conf->global->TICKET_URL_PUBLIC_INTERFACE ? $conf->global->TICKET_URL_PUBLIC_INTERFACE . '/view.php' : dol_buildpath('/public/ticket/view.php', 2)) . '?track_id=' . $object->track_id; - $message .= "\n\n" . $langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : ' . $url_public_ticket . "\n"; - - // Add signature - $message .= '\n\n' . $message_signature; - - if (!empty($object->origin_email) && !in_array($object->origin_email, $sendto)) { - $sendto[] = $object->origin_email; - } - if ($object->fk_soc > 0 && !in_array($object->origin_email, $sendto)) { - $sendto[] = $object->thirdparty->email; - } - $this->sendTicketMessageByEmail($subject, $message, '', $sendto); - } - - $this->copyFilesForTicket(); - - $url = 'view.php?action=view_ticket&track_id=' . $object->track_id; - header("Location: " . $url); - exit; - } else { - setEventMessages($object->error, $object->errors, 'errors'); - } - } else { - setEventMessages($this->error, $this->errors, 'errors'); - } - } - /** * Fetch object * @@ -1070,54 +227,6 @@ class ActionsTicket } } - /** - * View list of logs with timeline view - * - * @param boolean $show_user Show user who make action - * @param Ticket $object Object - * @return void - */ - public function viewTimelineTicketLogs($show_user = true, $object = true) - { - global $conf, $langs; - - // Load logs in cache - $ret = $object->loadCacheLogsTicket(); - - if (is_array($object->cache_logs_ticket) && count($object->cache_logs_ticket) > 0) { - print '
'; - - foreach ($object->cache_logs_ticket as $id => $arraylogs) { - print '
'; - print '
'; - //print ''; - print '
'; - - print '
'; - print dol_nl2br($arraylogs['message']); - - print ''; - print dol_print_date($arraylogs['datec'], 'dayhour'); - - if ($show_user) { - if ($arraylogs['fk_user_create'] > 0) { - $userstat = new User($this->db); - $res = $userstat->fetch($arraylogs['fk_user_create']); - if ($res) { - print '
'.$userstat->getNomUrl(1).''; - } - } - } - print '
'; - print '
'; - print '
'; - } - print '
'; - } else { - print '
' . $langs->trans('NoLogForThisTicket') . '
'; - } - } - /** * Show ticket original message * @@ -1129,6 +238,8 @@ class ActionsTicket public function viewTicketOriginalMessage($user, $action, $object) { global $langs; + + print ''."\n"; if (!empty($user->rights->ticket->manage) && $action == 'edit_message_init') { // MESSAGE @@ -1169,14 +280,15 @@ class ActionsTicket //print '
' . $object->message . '
'; } + if ($user->rights->ticket->manage && $action == 'edit_message_init') { + print '
'; + print ' '; + print ' '; + print '
'; + } print ''; print ''; print ''; - if ($user->rights->ticket->manage && $action == 'edit_message_init') { - print ' '; - print ' '; - print '
'; - } } /** * View html list of message for ticket @@ -1307,151 +419,6 @@ class ActionsTicket } } - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * load_previous_next_ref - * - * @param string $filter Filter - * @param int $fieldid Id - * @return int 0 - */ - public function load_previous_next_ref($filter, $fieldid) - { - // phpcs:enable - $this->getInstanceDao(); - return $object->load_previous_next_ref($filter, $fieldid); - } - - /** - * Send ticket by email to linked contacts - * - * @param string $subject Email subject - * @param string $message Email message - * @param int $send_internal_cc Receive a copy on internal email ($conf->global->TICKET_NOTIFICATION_EMAIL_FROM) - * @param array $array_receiver Array of receiver. exemple array('name' => 'John Doe', 'email' => 'john@doe.com', etc...) - * @return void - */ - public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array()) - { - global $conf, $langs; - - if ($conf->global->TICKET_DISABLE_ALL_MAILS) { - dol_syslog(get_class($this) . '::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKETSUP_DISABLE_ALL_MAILS', LOG_WARNING); - return ''; - } - - $langs->load("mails"); - - if (!class_exists('Contact')) { - include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; - } - - $contactstatic = new Contact($this->db); - - // If no receiver defined, load all ticket linked contacts - if (!is_array($array_receiver) || !count($array_receiver) > 0) { - $array_receiver = $object->getInfosTicketInternalContact(); - $array_receiver = array_merge($array_receiver, $object->getInfosTicketExternalContact()); - } - - if ($send_internal_cc) { - $sendtocc = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM; - } - - $from = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM; - if (is_array($array_receiver) && count($array_receiver) > 0) { - foreach ($array_receiver as $key => $receiver) { - // Create form object - include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; - $formmail = new FormMail($this->db); - - $attachedfiles = $formmail->get_attached_files(); - $filepath = $attachedfiles['paths']; - $filename = $attachedfiles['names']; - $mimetype = $attachedfiles['mimes']; - - $message_to_send = dol_nl2br($message); - - // Envoi du mail - if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { - $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; - $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; - } - include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php'; - $mailfile = new CMailFile($subject, $receiver, $from, $message_to_send, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1); - if ($mailfile->error) { - setEventMessages($mailfile->error, null, 'errors'); - } else { - $result = $mailfile->sendfile(); - if ($result) { - setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs'); - } else { - $langs->load("other"); - if ($mailfile->error) { - setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors'); - dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver) . ' : ' . $mailfile->error); - } else { - setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors'); - } - } - } - if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { - $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; - } - } - } else { - $langs->load("other"); - setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings'); - } - } - - /** - * Copy files into ticket directory - * Used for files linked into messages - * - * @return void - */ - public function copyFilesForTicket() - { - global $conf, $object; - - // Create form object - include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; - include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; - include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; - - $maxwidthsmall = 270; - $maxheightsmall = 150; - $maxwidthmini = 128; - $maxheightmini = 72; - - $formmail = new FormMail($this->db); - - $attachedfiles = $formmail->get_attached_files(); - $filepath = $attachedfiles['paths']; - $filename = $attachedfiles['names']; - $mimetype = $attachedfiles['mimes']; - - // Copy files into ticket directory - $destdir = $conf->ticket->dir_output . '/' . $object->track_id; - - if (!dol_is_dir($destdir)) { - dol_mkdir($destdir); - } - foreach ($filename as $i => $val) { - $res = dol_move($filepath[$i], $destdir . '/' . $filename[$i]); - if (image_format_supported($destdir . '/' . $filename[$i]) == 1) { - // Create small thumbs for image (Ratio is near 16/9) - // Used on logon for example - $imgThumbSmall = vignette($destdir . '/' . $filename[$i], $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs"); - // Create mini thumbs for image (Ratio is near 16/9) - // Used on menu or for setup page for example - $imgThumbMini = vignette($destdir . '/' . $filename[$i], $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs"); - } - $formmail->remove_attached_files($i); - } - } - /** * Print html navbar with link to set ticket status * @@ -1476,8 +443,7 @@ class ActionsTicket // Sort results to be similar to status object list //sort($exclude_status); - //print '
'; - foreach ($object->statuts_short as $status => $statut_label) { + foreach ($object->statuts_short as $status => $status_label) { if (!in_array($status, $exclude_status)) { print '
'; @@ -1490,7 +456,7 @@ class ActionsTicket $urlforbutton = $_SERVER['PHP_SELF'] . '?track_id=' . $object->track_id . '&action=set_status&new_status=' . $status; } - print ''; + print ''; print img_picto($langs->trans($object->statuts_short[$status]), 'statut' . $status . '.png@ticket') . ' ' . $langs->trans($object->statuts_short[$status]); print ''; print '
'; @@ -1499,17 +465,6 @@ class ActionsTicket print '

'; } - - /** - * deleteObjectLinked - * - * @return number - */ - public function deleteObjectLinked() - { - return $this->dao->deleteObjectLinked(); - } - /** * Hook to add email element template * diff --git a/htdocs/ticket/class/api_tickets.class.php b/htdocs/ticket/class/api_tickets.class.php index fedadd2c96e..818514868c2 100644 --- a/htdocs/ticket/class/api_tickets.class.php +++ b/htdocs/ticket/class/api_tickets.class.php @@ -123,19 +123,14 @@ class Tickets extends DolibarrApi /** * Get properties of a Ticket object - * * Return an array with ticket informations * * @param int $id ID of ticket * @param string $track_id Tracking ID of ticket * @param string $ref Reference for ticket * @return array|mixed Data without useless information - * - * @throws 401 - * @throws 403 - * @throws 404 */ - public function getCommon($id = 0, $track_id = '', $ref = '') + private function getCommon($id = 0, $track_id = '', $ref = '') { if (! DolibarrApiAccess::$user->rights->ticket->read) { throw new RestException(403); diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index c603c0489cb..c049712d623 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -25,8 +25,7 @@ // Put here all includes required by your class file require_once DOL_DOCUMENT_ROOT . "/core/class/commonobject.class.php"; require_once DOL_DOCUMENT_ROOT . '/fichinter/class/fichinter.class.php'; -//require_once DOL_DOCUMENT_ROOT."/societe/class/societe.class.php"; -//require_once DOL_DOCUMENT_ROOT."/product/class/product.class.php"; +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php'; /** @@ -66,7 +65,7 @@ class Ticket extends CommonObject /** - * @var string Hash to identify ticket + * @var string Hash to identify ticket publically */ public $track_id; @@ -186,19 +185,19 @@ class Ticket extends CommonObject 'origin_email' => array('type'=>'mail', 'label'=>'OriginEmail', 'visible'=>-2, 'enabled'=>1, 'position'=>16, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object"), 'subject' => array('type'=>'varchar(255)', 'label'=>'Subject', 'visible'=>1, 'enabled'=>1, 'position'=>18, 'notnull'=>-1, 'searchall'=>1, 'help'=>""), 'type_code' => array('type'=>'varchar(32)', 'label'=>'Type', 'visible'=>1, 'enabled'=>1, 'position'=>20, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth100'), - 'category_code' => array('type'=>'varchar(32)', 'label'=>'TicketGroup', 'visible'=>-1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth100'), - 'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth100'), + 'category_code' => array('type'=>'varchar(32)', 'label'=>'TicketGroup', 'visible'=>-1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'), + 'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'), 'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"LinkToThirparty"), 'notify_tiers_at_create' => array('type'=>'integer', 'label'=>'NotifyThirdparty', 'visible'=>-1, 'enabled'=>0, 'position'=>51, 'notnull'=>1, 'index'=>1), - 'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php', 'label'=>'Project', 'visible'=>-1, 'enabled'=>1, 'position'=>52, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"LinkToProject"), - 'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'searchall'=>1, 'help'=>""), + 'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php', 'label'=>'Project', 'visible'=>-1, 'enabled'=>1, 'position'=>52, 'notnull'=>-1, 'index'=>1, 'help'=>"LinkToProject"), + 'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""), 'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1), 'date_read' => array('type'=>'datetime', 'label'=>'TicketReadOn', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1), 'fk_user_assign' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'AssignedTo', 'visible'=>1, 'enabled'=>1, 'position'=>505, 'notnull'=>1), - 'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>1, 'enabled'=>1, 'position'=>510, 'notnull'=>1), + 'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>-1, 'enabled'=>1, 'position'=>510, 'notnull'=>1), 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-1, 'enabled'=>1, 'position'=>520, 'notnull'=>1), 'message' => array('type'=>'text', 'label'=>'Message', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1,), - 'progress' => array('type'=>'varchar(100)', 'label'=>'Progression', 'visible'=>-1, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'searchall'=>1, 'css'=>'right', 'help'=>""), + 'progress' => array('type'=>'varchar(100)', 'label'=>'Progression', 'visible'=>-1, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'css'=>'right', 'help'=>""), 'resolution' => array('type'=>'integer', 'label'=>'Resolution', 'visible'=>-1, 'enabled'=>1, 'position'=>550, 'notnull'=>1), 'fk_statut' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>600, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'Closed', 9 => 'Deleted')) ); @@ -208,10 +207,10 @@ class Ticket extends CommonObject */ const STATUS_NOT_READ = 0; const STATUS_READ = 1; - const STATUS_ANSWERED = 3; - const STATUS_ASSIGNED = 4; - const STATUS_IN_PROGRESS = 5; - const STATUS_WAITING = 6; + const STATUS_ASSIGNED = 2; + const STATUS_IN_PROGRESS = 3; + const STATUS_NEED_MORE_INFO = 5; + const STATUS_WAITING = 7; const STATUS_CLOSED = 8; const STATUS_CANCELED = 9; @@ -225,8 +224,8 @@ class Ticket extends CommonObject { $this->db = $db; - $this->statuts_short = array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'Closed', 9 => 'Deleted'); - $this->statuts = array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'Closed', 9 => 'Deleted'); + $this->statuts_short = array(self::STATUS_NOT_READ => 'Unread', self::STATUS_READ => 'Read', self::STATUS_ASSIGNED => 'Assigned', self::STATUS_IN_PROGRESS => 'InProgress', self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation', self::STATUS_WAITING => 'Waiting', self::STATUS_CLOSED => 'Closed', self::STATUS_CANCELED => 'Canceled'); + $this->statuts = array(self::STATUS_NOT_READ => 'Unread', self::STATUS_READ => 'Read', self::STATUS_ASSIGNED => 'Assigned', self::STATUS_IN_PROGRESS => 'InProgress', self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation', self::STATUS_WAITING => 'Waiting', self::STATUS_CLOSED => 'Closed', self::STATUS_CANCELED => 'Canceled'); } /** @@ -327,11 +326,14 @@ class Ticket extends CommonObject global $conf, $langs; $error = 0; + // Clean parameters $this->datec = dol_now(); + if (empty($this->track_id)) $this->track_id = generate_random_id(16); // Check more parameters // If error, this->errors[] is filled $result = $this->verify(); + if ($result >= 0) { // Insert request $sql = "INSERT INTO " . MAIN_DB_PREFIX . "ticket("; @@ -904,12 +906,6 @@ class Ticket extends CommonObject if ($res < 0) $error++; } - if (!$error) { - $sql = "DELETE FROM " . MAIN_DB_PREFIX . "ticket_msg"; - $sql .= " WHERE fk_track_id = '" . $this->db->escape($this->track_id) . "'"; - $resql = $this->db->query($sql); - } - // Removed extrafields if (!$error) { $result = $this->deleteExtraFields(); @@ -948,13 +944,12 @@ class Ticket extends CommonObject /** * Load an object from its id and create a new one in database * - * @param int $fromid Id of object to clone - * @return int New id of clone + * @param User $user User that clone + * @param int $fromid Id of object to clone + * @return int New id of clone */ - public function createFromClone($fromid) + public function createFromClone(User $user, $fromid) { - global $user, $langs; - $error = 0; $object = new Ticket($this->db); @@ -1175,7 +1170,7 @@ class Ticket extends CommonObject * Return status label of object * * @param string $statut id statut - * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto * @return string Label */ public function LibStatut($statut, $mode = 0) @@ -1190,134 +1185,106 @@ class Ticket extends CommonObject return $langs->trans($this->statuts_short[$statut]); } elseif ($mode == 2) { - if ($statut == 0) { + if ($statut == self::STATUS_NOT_READ) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 1) { + elseif ($statut == self::STATUS_READ) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 3) { + elseif ($statut == self::STATUS_ASSIGNED) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut2.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + elseif ($statut == self::STATUS_IN_PROGRESS) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 4) { - return img_picto($langs->trans($this->statuts_short[$statut]), 'statut4.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); - } - - elseif ($statut == 5) { + elseif ($statut == self::STATUS_NEED_MORE_INFO) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 6) { + elseif ($statut == self::STATUS_WAITING) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 8) { + elseif ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 9) { + elseif ($statut == self::STATUS_CANCELED) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } } elseif ($mode == 3) { - if ($statut == 0) { + if ($statut == self::STATUS_NOT_READ) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticket'); } - - elseif ($statut == 1) { + elseif ($statut == self::STATUS_READ) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticket'); } - - elseif ($statut == 3) { + elseif ($statut == self::STATUS_ASSIGNED) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut2.png@ticket'); + } + elseif ($statut == self::STATUS_IN_PROGRESS) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticket'); } - - elseif ($statut == 4) { - return img_picto($langs->trans($this->statuts_short[$statut]), 'statut4.png@ticket'); - } - - elseif ($statut == 5) { + elseif ($statut == self::STATUS_NEED_MORE_INFO) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticket'); } - - elseif ($statut == 6) { + elseif ($statut == self::STATUS_WAITING) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticket'); } - - elseif ($statut == 8) { + elseif ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticket'); } - - elseif ($statut == 9) { + elseif ($statut == self::STATUS_CANCELED) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticket'); } } elseif ($mode == 4) { - if ($statut == 0) { + if ($statut == self::STATUS_NOT_READ) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 1) { + elseif ($statut == self::STATUS_READ) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 3) { + elseif ($statut == self::STATUS_ASSIGNED) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut2.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + elseif ($statut == self::STATUS_IN_PROGRESS) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 4) { - return img_picto($langs->trans($this->statuts_short[$statut]), 'statut4.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); - } - - elseif ($statut == 5) { + elseif ($statut == self::STATUS_NEED_MORE_INFO) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 6) { + elseif ($statut == self::STATUS_WAITING) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 8) { + elseif ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } - - elseif ($statut == 9) { + elseif ($statut == self::STATUS_CANCELED) { return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]); } } - elseif ($mode == 5) { - if ($statut == 0) { + elseif ($mode == 5 || $mode == 6) { + if ($statut == self::STATUS_NOT_READ) { return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticket'); } - - elseif ($statut == 1) { + elseif ($statut == self::STATUS_READ) { return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticket'); } - - elseif ($statut == 3) { + elseif ($statut == self::STATUS_ASSIGNED) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut2.png@ticket'); + } + elseif ($statut == self::STATUS_IN_PROGRESS) { return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticket'); } - - elseif ($statut == 4) { - return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut4.png@ticket'); - } - - elseif ($statut == 5) { + elseif ($statut == self::STATUS_NEED_MORE_INFO) { return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticket'); } - - elseif ($statut == 6) { + elseif ($statut == self::STATUS_WAITING) { return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticket'); } - - elseif ($statut == 8) { + elseif ($statut == self::STATUS_CLOSED) { return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticket'); } - - elseif ($statut == 9) { + elseif ($statut == self::STATUS_CANCELED) { return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticket'); } } @@ -1644,6 +1611,8 @@ class Ticket extends CommonObject global $conf, $langs; $error = 0; + $now = dol_now(); + // Clean parameters if (isset($this->fk_track_id)) { $this->fk_track_id = trim($this->fk_track_id); @@ -1653,50 +1622,33 @@ class Ticket extends CommonObject $this->message = trim($this->message); } - // Insert request - $sql = "INSERT INTO " . MAIN_DB_PREFIX . "ticket_msg("; - - $sql .= "fk_track_id,"; - $sql .= "fk_user_action,"; - $sql .= "datec,"; - $sql .= "message,"; - $sql .= "private"; - $sql .= ") VALUES ("; - $sql .= " " . (!isset($this->fk_track_id) ? "'" . $this->db->escape($this->track_id) . "'" : "'" . $this->db->escape($this->fk_track_id) . "'") . ","; - $sql .= " " . ($this->fk_user_action > 0 ? $this->fk_user_action : $user->id) . ","; - $sql .= " '" . $this->db->idate(dol_now()) . "',"; - $sql .= " " . (!isset($this->message) ? 'NULL' : "'" . $this->db->escape($this->message) . "'") . ","; - $sql .= " " . (empty($this->private) ? '0' : "'" . $this->db->escape($this->private) . "'") . ""; - $sql .= ")"; - $this->db->begin(); - dol_syslog(get_class($this) . "::create_ticket_message sql=" . $sql, LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { - $error++; - $this->errors[] = "Error " . $this->db->lasterror(); - } + // Insert entry into agenda with code 'TICKET_MSG' + include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; + $actioncomm=new ActionComm($this->db); + $actioncomm->type_code = 'AC_OTH_AUTO'; + $actioncomm->code = 'TICKET_MSG'; + $actioncomm->socid = $this->socid; + $actioncomm->label = $this->subject; + $actioncomm->note = $this->message; + $actioncomm->userassigned = array($user->id); + $actioncomm->userownerid = $user->id; + $actioncomm->datep = $now; + $actioncomm->percentage = 100; + $actioncomm->elementtype = 'ticket'; + $actioncomm->fk_element = $this->id; - if (!$error) { - if (!$notrigger) { - // Uncomment this and change MYOBJECT to your own tag if you - // want this action calls a trigger. - //// Call triggers - //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; - //$interface=new Interfaces($this->db); - //$result=$interface->run_triggers('MYOBJECT_CREATE',$this,$user,$langs,$conf); - //if ($result < 0) { $error++; $this->errors=$interface->errors; } - //// End call triggers - } + $actionid = $actioncomm->create($user); + if ($actionid <= 0) + { + $error++; + $this->error = $actioncomm->error; + $this->errors = $actioncomm->errors; } // Commit or rollback if ($error) { - foreach ($this->errors as $errmsg) { - dol_syslog(get_class($this) . "::create_ticket_message " . $errmsg, LOG_ERR); - $this->error .= ($this->error ? ', ' . $errmsg : $errmsg); - } $this->db->rollback(); return -1 * $error; } else { @@ -1719,12 +1671,13 @@ class Ticket extends CommonObject } // Cache deja charge - $sql = "SELECT rowid, fk_user_action, datec, message, private"; - $sql .= " FROM " . MAIN_DB_PREFIX . "ticket_msg"; - $sql .= " WHERE fk_track_id ='" . $this->db->escape($this->track_id) . "'"; + $sql = "SELECT rowid, fk_user_author, datec, label, message, visibility"; + $sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm"; + $sql .= " WHERE fk_element = " . (int) $this->id; + $sql .= " AND elementtype = 'ticket'"; $sql .= " ORDER BY datec DESC"; - dol_syslog(get_class($this) . "::load_cache_actions_ticket sql=" . $sql, LOG_DEBUG); + dol_syslog(get_class($this) . "::load_cache_actions_ticket sql=" . $sql, LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) { $num = $this->db->num_rows($resql); @@ -1734,8 +1687,9 @@ class Ticket extends CommonObject $this->cache_msgs_ticket[$i]['id'] = $obj->rowid; $this->cache_msgs_ticket[$i]['fk_user_action'] = $obj->fk_user_action; $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec); + $this->cache_msgs_ticket[$i]['subject'] = $obj->label; $this->cache_msgs_ticket[$i]['message'] = $obj->message; - $this->cache_msgs_ticket[$i]['private'] = $obj->private; + $this->cache_msgs_ticket[$i]['private'] = ($obj->visibility == 'private' ? 1 : 0); $i++; } return $num; @@ -1749,11 +1703,12 @@ class Ticket extends CommonObject /** * Close a ticket * - * @return int <0 if KO, >0 if OK + * @param User $user User that close + * @return int <0 if KO, >0 if OK */ - public function close() + public function close(User $user) { - global $conf, $user, $langs; + global $conf, $langs; if ($this->fk_statut != 9) { // not closed $this->db->begin(); @@ -1774,7 +1729,7 @@ class Ticket extends CommonObject foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) { $fichinter = new Fichinter($this->db); $fichinter->fetch($fichinter_id); - if($fichinter->statut == 0) { + if ($fichinter->statut == 0) { $result = $fichinter->setValid($user); if (!$result) { $this->errors[] = $fichinter->error; @@ -2365,7 +2320,7 @@ class Ticket extends CommonObject global $conf; $defaultref = ''; - $modele = empty($conf->global->TICKETSUP_ADDON) ? 'mod_ticket_simple' : $conf->global->TICKETSUP_ADDON; + $modele = empty($conf->global->TICKET_ADDON) ? 'mod_ticket_simple' : $conf->global->TICKET_ADDON; // Search template files $file = ''; @@ -2430,6 +2385,354 @@ class Ticket extends CommonObject } return false; } + + + /** + * Copy files into ticket directory + * Used for files linked into messages + * + * @return void + */ + public function copyFilesForTicket() + { + global $conf; + + // Create form object + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; + + $maxwidthsmall = 270; + $maxheightsmall = 150; + $maxwidthmini = 128; + $maxheightmini = 72; + + $formmail = new FormMail($this->db); + + $attachedfiles = $formmail->get_attached_files(); + + $filepath = $attachedfiles['paths']; + $filename = $attachedfiles['names']; + $mimetype = $attachedfiles['mimes']; + + // Copy files into ticket directory + $destdir = $conf->ticket->dir_output . '/' . $this->ref; + + if (!dol_is_dir($destdir)) { + dol_mkdir($destdir); + } + foreach ($filename as $i => $val) { + $res = dol_move($filepath[$i], $destdir . '/' . $filename[$i]); + if (image_format_supported($destdir . '/' . $filename[$i]) == 1) { + // Create small thumbs for image (Ratio is near 16/9) + // Used on logon for example + $imgThumbSmall = vignette($destdir . '/' . $filename[$i], $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs"); + // Create mini thumbs for image (Ratio is near 16/9) + // Used on menu or for setup page for example + $imgThumbMini = vignette($destdir . '/' . $filename[$i], $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs"); + } + $formmail->remove_attached_files($i); + } + } + + + /** + * Add new message on a ticket (private area) + * + * @param User $user User for action + * @param string $action Action string + * @param int $private 1=Message is private. TODO Implement this. What does this means ? + * @return int + */ + public function newMessage($user, &$action, $private = 1) + { + global $mysoc, $conf, $langs; + + if (!class_exists('Contact')) { + include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; + } + + $contactstatic = new Contact($this->db); + $object = new Ticket($this->db); + + $error = 0; + + $ret = $object->fetch('', '', GETPOST('track_id', 'alpha')); + + $object->socid = $object->fk_soc; + $object->fetch_thirdparty(); + if ($ret < 0) { + $error++; + array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); + $action = ''; + } + + if (!GETPOST("message")) { + $error++; + array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message"))); + $action = 'add_message'; + } + + if (!$error) { + $object->subject = GETPOST('subject', 'alphanohtml'); + $object->message = GETPOST("message", "none"); + $object->private = GETPOST("private_message", "alpha"); + $send_email = GETPOST('send_email', 'int'); + + $id = $object->createTicketMessage($user); + if ($id <= 0) { + $error++; + $this->errors = $object->error; + $this->errors = $object->errors; + $action = 'add_message'; + } + + if (!$error && $id > 0) { + setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs'); + + /* + * Send email to linked contacts + */ + if ($send_email > 0) { + // Retrieve internal contact datas + $internal_contacts = $object->getInfosTicketInternalContact(); + $sendto = array(); + if (is_array($internal_contacts) && count($internal_contacts) > 0) { + // altairis: set default subject + $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE; + $subject = GETPOST('subject') ? GETPOST('subject') : '[' . $label_title . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); + + $message_intro = $langs->trans('TicketNotificationEmailBody', "#" . $object->id); + $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE; + + $message = $langs->trans('TicketMessageMailIntroText'); + $message .= "\n\n"; + $message .= GETPOST('message'); + + // Coordonnées client + $message .= "\n\n"; + $message .= "==============================================\n"; + $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty') . " : " . $object->thirdparty->name : ''; + $message .= !empty($object->thirdparty->town) ? "\n" . $langs->trans('Town') . " : " . $object->thirdparty->town : ''; + $message .= !empty($object->thirdparty->phone) ? "\n" . $langs->trans('Phone') . " : " . $object->thirdparty->phone : ''; + + // Build array to display recipient list + foreach ($internal_contacts as $key => $info_sendto) { + // altairis: avoid duplicate notifications + if ($info_sendto['id'] == $user->id) { + continue; + } + + if ($info_sendto['email'] != '') { + if(!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; + + //Contact type + $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; + $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); + } + } + $message .= "\n"; + // URL ticket + $url_internal_ticket = dol_buildpath('/ticket/card.php', 2) . '?track_id=' . $object->track_id; + + // altairis: make html link on url + $message .= "\n" . $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal') . ' : ' . '' . $object->track_id . '' . "\n"; + + // Add global email address recipient + // altairis: use new TICKET_NOTIFICATION_EMAIL_TO configuration variable + if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) { + if(!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO; + } + + // altairis: dont try to send email if no recipient + if (!empty($sendto)) { + $this->sendTicketMessageByEmail($subject, $message, '', $sendto); + } + } + + /* + * Email for externals users if not private + */ + if (empty($object->private)) { + // Retrieve email of all contacts (external) + $external_contacts = $object->getInfosTicketExternalContact(); + + // If no contact, get email from thirdparty + if (is_array($external_contacts) && count($external_contacts) === 0) { + if (!empty($object->fk_soc)) { + $object->fetch_thirdparty($object->fk_soc); + $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id)); + $external_contacts = array_merge($external_contacts, $array_company); + } elseif (empty($object->fk_soc) && !empty($object->origin_email)) { + $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id)); + $external_contacts = array_merge($external_contacts, $array_external); + } + } + + $sendto = array(); + if (is_array($external_contacts) && count($external_contacts) > 0) { + // altairis: get default subject for email to external contacts + $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE; + $subject = GETPOST('subject') ? GETPOST('subject') : '[' . $label_title . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); + + $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro') : $conf->global->TICKET_MESSAGE_MAIL_INTRO; + $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE; + + // We put intro after + $message = GETPOST('message'); + $message .= "\n\n"; + + foreach ($external_contacts as $key => $info_sendto) { + // altairis: avoid duplicate emails to external contacts + if ($info_sendto['id'] == $user->contactid) { + continue; + } + + if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) { + if(!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; + + $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; + $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); + } + } + + // If public interface is not enable, use link to internal page into mail + $url_public_ticket = (!empty($conf->global->TICKET_ENABLE_PUBLIC_INTERFACE) ? + (!empty($conf->global->TICKET_URL_PUBLIC_INTERFACE) ? + $conf->global->TICKET_URL_PUBLIC_INTERFACE . '/view.php' : + dol_buildpath('/public/ticket/view.php', 2) + ) : + dol_buildpath('/ticket/card.php', 2) + ) . '?track_id=' . $object->track_id; + $message .= "\n" . $langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : ' . '' . $object->track_id . '' . "\n"; + + // Build final message + $message = $message_intro . $message; + + // Add signature + $message .= '
' . $message_signature; + + if (!empty($object->origin_email)) { + $sendto[] = $object->origin_email; + } + + if ($object->fk_soc > 0 && ! in_array($object->origin_email, $sendto)) { + $object->socid = $object->fk_soc; + $object->fetch_thirdparty(); + if(!empty($object->thirdparty->email)) $sendto[] = $object->thirdparty->email; + } + + // altairis: Add global email address reciepient + if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) { + if(!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO; + } + + // altairis: dont try to send email when no recipient + if (!empty($sendto)) { + $this->sendTicketMessageByEmail($subject, $message, '', $sendto); + } + } + } + } + + $object->copyFilesForTicket(); + + // Set status to "answered" if not set yet, only for internal users + if ($object->fk_statut < 3 && !$user->societe_id) { + $object->setStatut(3); + } + + return 1; + } else { + setEventMessages($object->error, $object->errors, 'errors'); + return -1; + } + } else { + setEventMessages($this->error, $this->errors, 'errors'); + return -1; + } + } + + + /** + * Send ticket by email to linked contacts + * + * @param string $subject Email subject + * @param string $message Email message + * @param int $send_internal_cc Receive a copy on internal email ($conf->global->TICKET_NOTIFICATION_EMAIL_FROM) + * @param array $array_receiver Array of receiver. exemple array('name' => 'John Doe', 'email' => 'john@doe.com', etc...) + * @return void + */ + public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array()) + { + global $conf, $langs; + + if ($conf->global->TICKET_DISABLE_ALL_MAILS) { + dol_syslog(get_class($this) . '::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKETSUP_DISABLE_ALL_MAILS', LOG_WARNING); + return ''; + } + + $langs->load("mails"); + + include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; + //$contactstatic = new Contact($this->db); + + // If no receiver defined, load all ticket linked contacts + if (!is_array($array_receiver) || !count($array_receiver) > 0) { + $array_receiver = $this->getInfosTicketInternalContact(); + $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact()); + } + + if ($send_internal_cc) { + $sendtocc = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM; + } + + $from = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM; + if (is_array($array_receiver) && count($array_receiver) > 0) { + foreach ($array_receiver as $key => $receiver) { + // Create form object + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + $formmail = new FormMail($this->db); + + $attachedfiles = $formmail->get_attached_files(); + $filepath = $attachedfiles['paths']; + $filename = $attachedfiles['names']; + $mimetype = $attachedfiles['mimes']; + + $message_to_send = dol_nl2br($message); + + // Envoi du mail + if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php'; + $mailfile = new CMailFile($subject, $receiver, $from, $message_to_send, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1); + if ($mailfile->error) { + setEventMessages($mailfile->error, null, 'errors'); + } else { + $result = $mailfile->sendfile(); + if ($result) { + setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs'); + } else { + $langs->load("other"); + if ($mailfile->error) { + setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors'); + dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver) . ' : ' . $mailfile->error); + } else { + setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors'); + } + } + } + if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + } + } else { + $langs->load("other"); + setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings'); + } + } } diff --git a/htdocs/ticket/class/ticketstats.class.php b/htdocs/ticket/class/ticketstats.class.php index 2f2e04baba9..469d44bc6d5 100644 --- a/htdocs/ticket/class/ticketstats.class.php +++ b/htdocs/ticket/class/ticketstats.class.php @@ -63,13 +63,13 @@ class TicketStats extends Stats $this->where = " fk_statut > 0"; $this->where .= " AND entity = " . $conf->entity; - if ($this->socid) { + if ($this->socid > 0) { $this->where .= " AND fk_soc = " . $this->socid; } if (is_array($this->userid) && count($this->userid) > 0) { - $this->where .= ' AND fk_user IN (' . join(',', $this->userid) . ')'; + $this->where .= ' AND fk_user_create IN (' . join(',', $this->userid) . ')'; } elseif ($this->userid > 0) { - $this->where .= ' AND fk_user = ' . $this->userid; + $this->where .= ' AND fk_user_create = ' . $this->userid; } } diff --git a/htdocs/ticket/contact.php b/htdocs/ticket/contact.php index 21c1087f276..f38a5abd910 100644 --- a/htdocs/ticket/contact.php +++ b/htdocs/ticket/contact.php @@ -50,9 +50,6 @@ $source = GETPOST('source', 'alpha'); $ligne = GETPOST('ligne', 'int'); $lineid = GETPOST('lineid', 'int'); - - - // Protection if external user if ($user->societe_id > 0) { $socid = $user->societe_id; @@ -64,8 +61,9 @@ $url_page_current = dol_buildpath('/ticket/contact.php', 1); $object = new Ticket($db); + /* - * Ajout d'un nouveau contact + * Actions */ if ($action == 'addcontact' && $user->rights->ticket->write) { @@ -110,9 +108,12 @@ if ($action == 'deletecontact' && $user->rights->ticket->write) { } } + + /* * View */ + $help_url = 'FR:DocumentationModuleTicket'; llxHeader('', $langs->trans("TicketContacts"), $help_url); @@ -121,12 +122,6 @@ $formcompany = new FormCompany($db); $contactstatic = new Contact($db); $userstatic = new User($db); -/* *************************************************************************** */ -/* */ -/* Mode vue et edition */ -/* */ -/* *************************************************************************** */ - if ($id > 0 || !empty($track_id) || !empty($ref)) { if ($object->fetch($id, $ref, $track_id) > 0) { @@ -146,7 +141,7 @@ if ($id > 0 || !empty($track_id) || !empty($ref)) { $head = ticket_prepare_head($object); - dol_fiche_head($head, 'contact', $langs->trans("Ticket"), 0, 'ticket'); + dol_fiche_head($head, 'contact', $langs->trans("Ticket"), -1, 'ticket'); $morehtmlref ='
'; $morehtmlref.= $object->subject; diff --git a/htdocs/ticket/document.php b/htdocs/ticket/document.php index b02b6ee07a7..16fabc6943c 100644 --- a/htdocs/ticket/document.php +++ b/htdocs/ticket/document.php @@ -61,15 +61,10 @@ if (! $sortfield) $sortfield="position_name"; $object = new Ticket($db); $result = $object->fetch($id, $ref, $track_id); -// to match document rules and compatibility -$old_ref = $object->ref; -$object->ref = $object->track_id; - - if ($result < 0) { setEventMessages($object->error, $object->errors, 'errors'); } else { - $upload_dir = $conf->ticket->dir_output . "/" . dol_sanitizeFileName($object->track_id); + $upload_dir = $conf->ticket->dir_output . "/" . dol_sanitizeFileName($object->ref); } @@ -79,8 +74,6 @@ if ($result < 0) { include_once DOL_DOCUMENT_ROOT . '/core/actions_linkedfiles.inc.php'; -$object->ref = $old_ref; - /* @@ -193,7 +186,7 @@ if ($object->id) $totalsize += $file['size']; } - $object->ref = $object->track_id; // For compatibility we use track ID for directory + //$object->ref = $object->track_id; // For compatibility we use track ID for directory $modulepart = 'ticket'; $permission = $user->rights->ticket->write; $permtoedit = $user->rights->ticket->write; diff --git a/htdocs/ticket/img/statut0.png b/htdocs/ticket/img/statut0.png index 6e631dc0086..b1cf5df0e75 100644 Binary files a/htdocs/ticket/img/statut0.png and b/htdocs/ticket/img/statut0.png differ diff --git a/htdocs/ticket/img/statut1.png b/htdocs/ticket/img/statut1.png index 5bff8090beb..4e72abd23cc 100644 Binary files a/htdocs/ticket/img/statut1.png and b/htdocs/ticket/img/statut1.png differ diff --git a/htdocs/ticket/img/statut4.png b/htdocs/ticket/img/statut2.png similarity index 100% rename from htdocs/ticket/img/statut4.png rename to htdocs/ticket/img/statut2.png diff --git a/htdocs/ticket/img/statut3.png b/htdocs/ticket/img/statut3.png index 4e72abd23cc..6ea7f62a9ff 100644 Binary files a/htdocs/ticket/img/statut3.png and b/htdocs/ticket/img/statut3.png differ diff --git a/htdocs/ticket/img/statut5.png b/htdocs/ticket/img/statut5.png index 6ea7f62a9ff..6e631dc0086 100644 Binary files a/htdocs/ticket/img/statut5.png and b/htdocs/ticket/img/statut5.png differ diff --git a/htdocs/ticket/img/statut6.png b/htdocs/ticket/img/statut6.png index af7f6d433dc..5bff8090beb 100644 Binary files a/htdocs/ticket/img/statut6.png and b/htdocs/ticket/img/statut6.png differ diff --git a/htdocs/ticket/img/statut8.png b/htdocs/ticket/img/statut8.png index 84f3c5e7fc5..af7f6d433dc 100644 Binary files a/htdocs/ticket/img/statut8.png and b/htdocs/ticket/img/statut8.png differ diff --git a/htdocs/ticket/img/statut9.png b/htdocs/ticket/img/statut9.png new file mode 100644 index 00000000000..84f3c5e7fc5 Binary files /dev/null and b/htdocs/ticket/img/statut9.png differ diff --git a/htdocs/ticket/list.php b/htdocs/ticket/list.php index 429055b2f27..96af20dc591 100644 --- a/htdocs/ticket/list.php +++ b/htdocs/ticket/list.php @@ -52,7 +52,7 @@ $id = GETPOST('id', 'int'); $msg_id = GETPOST('msg_id', 'int'); $socid = GETPOST('socid', 'int'); $projectid = GETPOST('projectid', 'int'); -$search_fk_soc=GETPOST('$search_fk_soc', 'int')?GETPOST('$search_fk_soc', 'int'):GETPOST('socid', 'int'); +$search_societe = GETPOST('search_societe', 'alpha'); $search_fk_project=GETPOST('search_fk_project', 'int')?GETPOST('search_fk_project', 'int'):GETPOST('projectid', 'int'); $search_fk_status = GETPOST('search_fk_statut', 'array'); $mode = GETPOST('mode', 'alpha'); @@ -85,7 +85,7 @@ if (! $sortorder) $sortorder="DESC"; if (GETPOST('search_fk_status', 'alpha') == 'non_closed') $_GET['search_fk_statut'][]='openall'; // For backward compatibility // Initialize array of search criterias -$search_all=trim(GETPOST("search_all", 'alpha')); +$search_all=trim(GETPOSTISSET("search_all")?GETPOSTISSET("search_all", 'alpha'):GETPOST('sall')); $search=array(); foreach($object->fields as $key => $val) { @@ -190,9 +190,6 @@ $socstatic = new Societe($db); $help_url = ''; $title = $langs->trans('TicketList'); -llxHeader('', $title, $help_url); - - // Build and execute select // -------------------------------------------------------------------- @@ -211,12 +208,14 @@ $sql.=$hookmanager->resPrint; $sql=preg_replace('/, $/', '', $sql); $sql.= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t"; if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)"; +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)"; if ($object->ismultientitymanaged == 1) $sql.= " WHERE t.entity IN (".getEntity($object->element).")"; else $sql.=" WHERE 1 = 1"; + foreach($search as $key => $val) { - if ($key == 'fk_statut') + if ($key == 'fk_statut') { $tmpstatus=''; if ($search['fk_statut'] == 'openall' || in_array('openall', $search['fk_statut'])) $tmpstatus.=($tmpstatus?',':'')."'0', '1', '3', '4', '5', '6'"; @@ -225,11 +224,16 @@ foreach($search as $key => $val) elseif (is_array($search[$key]) && count($search[$key])) $sql.=natural_search($key, join(',', $search[$key]), 2); continue; } + if ($key == 'fk_user_assign') + { + if ($search[$key] > 0) $sql.=natural_search($key, $search[$key], 2); + continue; + } $mode_search=(($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key]))?1:0); - if ($search[$key] != '') $sql.=natural_search($key, $search[$key], (($key == 'fk_statut')?2:$mode_search)); + if ($search[$key] != '') $sql.=natural_search($key, $search[$key], $mode_search); } if ($search_all) $sql.= natural_search(array_keys($fieldstosearchall), $search_all); -if ($search_fk_soc) $sql.= natural_search('fk_soc', $search_fk_soc, 2); +if ($search_societe) $sql .= natural_search('s.nom', $search_societe); if ($search_fk_project) $sql.= natural_search('fk_project', $search_fk_project, 2); if (! $user->societe_id && ($mode == "mine" || (!$user->admin && $conf->global->TICKET_LIMIT_VIEW_ASSIGNED_ONLY))) { $sql.= " AND (t.fk_user_assign = ".$user->id; @@ -244,22 +248,6 @@ $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook $sql.=$hookmanager->resPrint; -/* If a group by is required -$sql.= " GROUP BY " -foreach($object->fields as $key => $val) -{ - $sql.='t.'.$key.', '; -} -// Add fields from extrafields -if (! empty($extrafields->attributes[$object->table_element]['label'])) { - foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.', ' : ''); -// Add where from hooks -$parameters=array(); -$reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters); // Note that $action and $object may have been modified by hook -$sql.=$hookmanager->resPrint; -$sql=preg_replace('/, $/','', $sql); -*/ - $sql.=$db->order($sortfield, $sortorder); // Count total nb of records @@ -306,6 +294,9 @@ if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && // Output page // -------------------------------------------------------------------- +llxHeader('', $title, $help_url); + + if ($socid && !$projectid && $user->rights->societe->lire) { $socstat = new Societe($db); $res = $socstat->fetch($socid); @@ -442,7 +433,7 @@ $arrayofmassactions = array( //'presend'=>$langs->trans("SendByMail"), //'builddoc'=>$langs->trans("PDFMerge"), ); -if ($user->rights->ticket->delete) $arrayofmassactions['predelete']=$langs->trans("Delete"); +if ($user->rights->ticket->delete) $arrayofmassactions['predelete']=''.$langs->trans("Delete"); if (GETPOST('nomassaction', 'int') || in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); @@ -479,10 +470,10 @@ $objecttmp=new Ticket($db); $trackid='tick'.$object->id; include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; -if ($sall) +if ($search_all) { foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val); - print '
'.$langs->trans("FilterOnInto", $sall) . join(', ', $fieldstosearchall).'
'; + print '
'.$langs->trans("FilterOnInto", $search_all) . join(', ', $fieldstosearchall).'
'; } $moreforfilter = ''; @@ -549,6 +540,10 @@ foreach($object->fields as $key => $val) print Form::multiselectarray('search_fk_statut', $arrayofstatus, array_values($search[$key]), 0, 0, 'minwidth150', 1, 0, '', '', ''); print ''; } + elseif ($key == "fk_soc") + { + print ''; + } else { print ''; } @@ -640,6 +635,10 @@ while ($i < min($num, $limit)) if ($cssforfield || $val['css']) print '"'; print '>'; if ($key == 'fk_statut') print $object->getLibStatut(5); + elseif ($key == 'category_code') print $langs->getLabelFromKey($db, $object->category_code, 'c_ticket_category', 'code', 'label'); + elseif ($key == 'severity_code') print $langs->getLabelFromKey($db, $object->severity_code, 'c_ticket_severity', 'code', 'label'); + elseif ($key == 'type_code') print $langs->getLabelFromKey($db, $object->type_code, 'c_ticket_type', 'code', 'label'); + elseif ($key == 'tms') print dol_print_date($db->jdate($obj->$key), 'dayhour', 'tzuser'); elseif (in_array($val['type'], array('date','datetime','timestamp'))) print $object->showOutputField($val, $key, $db->jdate($obj->$key), ''); else print $object->showOutputField($val, $key, $obj->$key, ''); print ''; diff --git a/htdocs/ticket/stats/index.php b/htdocs/ticket/stats/index.php index 5eb206057c3..de4ad035faf 100644 --- a/htdocs/ticket/stats/index.php +++ b/htdocs/ticket/stats/index.php @@ -24,6 +24,7 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticketstats.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php'; $WIDTH=DolGraph::getDefaultGraphSizeForStats('width'); @@ -57,7 +58,7 @@ $langs->loadLangs(array('orders', 'companies', 'other', 'tickets')); */ $form=new Form($db); -//$formticket=new FormTicket($db); +$object=new Ticket($db); $title=$langs->trans("Statistics"); $dir=$conf->ticket->dir_temp; @@ -69,7 +70,7 @@ print load_fiche_titre($title, '', 'title_ticket.png'); dol_mkdir($dir); $stats = new TicketStats($db, $socid, ($userid>0?$userid:0)); -if ($object_status != '' && $object_status >= -1) $stats->where .= ' AND c.fk_statut IN ('.$db->escape($object_status).')'; +if ($object_status != '' && $object_status >= -1) $stats->where .= ' AND fk_statut IN ('.$db->escape($object_status).')'; // Build graphic number of object @@ -242,16 +243,8 @@ print ''.$langs->trans("CreatedBy").' print $form->select_dolusers($userid, 'userid', 1, '', 0, '', '', 0, 0, 0, '', 0, '', 'maxwidth300'); // Status print ''.$langs->trans("Status").''; -$liststatus=array( - Ticket::STATUS_NOT_READ=>$langs->trans("StatusNotRead"), - Ticket::STATUS_READ=>$langs->trans("StatusRead"), - Ticket::STATUS_ASSIGNED=>$langs->trans("StatusAssigned"), - Ticket::STATUS_IN_PROGRESS=>$langs->trans("StatusInProgress"), - Ticket::STATUS_ANSWERED=>$langs->trans("StatusAnswered"), - Ticket::STATUS_CLOSED=>$langs->trans("StatusClosed"), - Ticket::STATUS_WAITING=>$langs->trans("StatusWaiting") - ); -print $form->selectarray('object_status', $liststatus, GETPOST('object_status'), -4); +$liststatus = $object->fields['fk_statut']['arrayofkeyval']; +print $form->selectarray('object_status', $liststatus, GETPOST('object_status', 'int'), -4, 0, 0, '', 1); print ''; // Year print ''.$langs->trans("Year").''; diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index f4652ef5a48..f6a8eb9f8d9 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -70,6 +70,7 @@ class User extends CommonObject public $gender; public $birth; public $email; + public $personal_email; public $skype; public $twitter; @@ -92,6 +93,7 @@ class User extends CommonObject public $office_phone; public $office_fax; public $user_mobile; + public $personal_mobile; public $admin; public $login; public $api_key; @@ -125,12 +127,12 @@ class User extends CommonObject //! If this is defined, it is an external user /** * @deprecated - * @see socid + * @see $socid */ public $societe_id; /** * @deprecated - * @see contactid + * @see $contactid */ public $contact_id; public $socid; @@ -145,6 +147,8 @@ class User extends CommonObject * @var int User ID */ public $fk_user; + public $fk_user_expense_validator; + public $fk_user_holiday_validator; public $clicktodial_url; public $clicktodial_login; @@ -242,12 +246,12 @@ class User extends CommonObject $login=trim($login); // Get user - $sql = "SELECT u.rowid, u.lastname, u.firstname, u.employee, u.gender, u.birth, u.email, u.job, u.skype, u.twitter, u.facebook, u.linkedin,"; - $sql.= " u.signature, u.office_phone, u.office_fax, u.user_mobile,"; + $sql = "SELECT u.rowid, u.lastname, u.firstname, u.employee, u.gender, u.birth, u.email, u.personal_email, u.job, u.skype, u.twitter, u.facebook, u.linkedin,"; + $sql.= " u.signature, u.office_phone, u.office_fax, u.user_mobile, u.personal_mobile,"; $sql.= " u.address, u.zip, u.town, u.fk_state as state_id, u.fk_country as country_id,"; $sql.= " u.admin, u.login, u.note,"; $sql.= " u.pass, u.pass_crypted, u.pass_temp, u.api_key,"; - $sql.= " u.fk_soc, u.fk_socpeople, u.fk_member, u.fk_user, u.ldap_sid,"; + $sql.= " u.fk_soc, u.fk_socpeople, u.fk_member, u.fk_user, u.ldap_sid, u.fk_user_expense_validator, u.fk_user_holiday_validator,"; $sql.= " u.statut, u.lang, u.entity,"; $sql.= " u.datec as datec,"; $sql.= " u.tms as datem,"; @@ -345,9 +349,11 @@ class User extends CommonObject $this->state = ($obj->state!='-'?$obj->state:''); $this->office_phone = $obj->office_phone; - $this->office_fax = $obj->office_fax; - $this->user_mobile = $obj->user_mobile; + $this->office_fax = $obj->office_fax; + $this->user_mobile = $obj->user_mobile; + $this->personal_mobile = $obj->personal_mobile; $this->email = $obj->email; + $this->personal_email = $obj->personal_email; $this->skype = $obj->skype; $this->twitter = $obj->twitter; $this->facebook = $obj->facebook; @@ -382,6 +388,8 @@ class User extends CommonObject $this->contactid = $obj->fk_socpeople; $this->fk_member = $obj->fk_member; $this->fk_user = $obj->fk_user; + $this->fk_user_expense_validator = $obj->fk_user_expense_validator; + $this->fk_user_holiday_validator = $obj->fk_user_holiday_validator; $this->default_range = $obj->default_range; $this->default_c_exp_tax_cat = $obj->default_c_exp_tax_cat; @@ -517,7 +525,7 @@ class User extends CommonObject * @param int $entity Entity to use * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers * @return int > 0 if OK, < 0 if KO - * @see clearrights, delrights, getrights + * @see clearrights(), delrights(), getrights() */ public function addrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0) { @@ -643,7 +651,7 @@ class User extends CommonObject * @param int $entity Entity to use * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers * @return int > 0 if OK, < 0 if OK - * @see clearrights, addrights, getrights + * @see clearrights(), addrights(), getrights() */ public function delrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0) { @@ -759,7 +767,7 @@ class User extends CommonObject * Clear all permissions array of user * * @return void - * @see getrights + * @see getrights() */ public function clearrights() { @@ -777,7 +785,7 @@ class User extends CommonObject * @param string $moduletag Limit permission for a particular module ('' by default means load all permissions) * @param int $forcereload Force reload of permissions even if they were already loaded (ignore cache) * @return void - * @see clearrights, delrights, addrights + * @see clearrights(), delrights(), addrights() */ public function getrights($moduletag = '', $forcereload = 0) { @@ -1482,7 +1490,9 @@ class User extends CommonObject $this->office_phone = trim($this->office_phone); $this->office_fax = trim($this->office_fax); $this->user_mobile = trim($this->user_mobile); + $this->personal_mobile = trim($this->personal_mobile); $this->email = trim($this->email); + $this->personal_email = trim($this->personal_email); $this->skype = trim($this->skype); $this->twitter = trim($this->twitter); @@ -1537,7 +1547,9 @@ class User extends CommonObject $sql.= ", office_phone = '".$this->db->escape($this->office_phone)."'"; $sql.= ", office_fax = '".$this->db->escape($this->office_fax)."'"; $sql.= ", user_mobile = '".$this->db->escape($this->user_mobile)."'"; + $sql.= ", personal_mobile = '".$this->db->escape($this->personal_mobile)."'"; $sql.= ", email = '".$this->db->escape($this->email)."'"; + $sql.= ", personal_email = '".$this->db->escape($this->personal_email)."'"; $sql.= ", skype = '".$this->db->escape($this->skype)."'"; $sql.= ", twitter = '".$this->db->escape($this->twitter)."'"; $sql.= ", facebook = '".$this->db->escape($this->facebook)."'"; @@ -1552,6 +1564,8 @@ class User extends CommonObject $sql.= ", photo = ".($this->photo?"'".$this->db->escape($this->photo)."'":"null"); $sql.= ", openid = ".($this->openid?"'".$this->db->escape($this->openid)."'":"null"); $sql.= ", fk_user = ".($this->fk_user > 0?"'".$this->db->escape($this->fk_user)."'":"null"); + $sql.= ", fk_user_expense_validator = ".($this->fk_user_expense_validator > 0?"'".$this->db->escape($this->fk_user_expense_validator)."'":"null"); + $sql.= ", fk_user_holiday_validator = ".($this->fk_user_holiday_validator > 0?"'".$this->db->escape($this->fk_user_holiday_validator)."'":"null"); if (isset($this->thm) || $this->thm != '') $sql.= ", thm= ".($this->thm != ''?"'".$this->db->escape($this->thm)."'":"null"); if (isset($this->tjm) || $this->tjm != '') $sql.= ", tjm= ".($this->tjm != ''?"'".$this->db->escape($this->tjm)."'":"null"); if (isset($this->salary) || $this->salary != '') $sql.= ", salary= ".($this->salary != ''?"'".$this->db->escape($this->salary)."'":"null"); @@ -2664,6 +2678,7 @@ class User extends CommonObject $this->gender='man'; $this->note='This is a note'; $this->email='email@specimen.com'; + $this->personal_email='personalemail@specimen.com'; $this->skype='skypepseudo'; $this->twitter='twitterpseudo'; $this->facebook='facebookpseudo'; @@ -2671,6 +2686,7 @@ class User extends CommonObject $this->office_phone='0999999999'; $this->office_fax='0999999998'; $this->user_mobile='0999999997'; + $this->personal_mobile='0999999996'; $this->admin=0; $this->login='dolibspec'; $this->pass='dolibspec'; @@ -2799,8 +2815,7 @@ class User extends CommonObject /** * Update user using data from the LDAP * - * @param ldapuser $ldapuser Ladp User - * + * @param Object $ldapuser Ladp User * @return int <0 if KO, >0 if OK */ public function update_ldap2dolibarr(&$ldapuser) @@ -2841,7 +2856,7 @@ class User extends CommonObject * Return and array with all instanciated first level children users of current user * * @return void - * @see getAllChildIds + * @see getAllChildIds() */ public function get_children() { @@ -3018,7 +3033,7 @@ class User extends CommonObject * * @param int $addcurrentuser 1=Add also current user id to the list. * @return array Array of user id lower than user (all levels under user). This overwrite this->users. - * @see get_children + * @see get_children() */ public function getAllChildIds($addcurrentuser = 0) { @@ -3115,14 +3130,13 @@ class User extends CommonObject // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** - * Charge indicateurs this->nb pour le tableau de bord + * Load metrics this->nb for dashboard * * @return int <0 if KO, >0 if OK */ public function load_state_board() { // phpcs:enable - global $conf; $this->nb=array(); @@ -3233,15 +3247,38 @@ class User extends CommonObject * @param int $offset page * @param array $filter Filter array. Example array('field'=>'valueforlike', 'customurl'=>...) * @param string $filtermode Filter mode (AND or OR) + * @param bool $entityfilter Activate entity filter * @return int <0 if KO, >0 if OK */ - public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = array(), $filtermode = 'AND') + public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = array(), $filtermode = 'AND', $entityfilter = false) { - global $conf; + global $conf, $user; $sql="SELECT t.rowid"; $sql.= ' FROM '.MAIN_DB_PREFIX .$this->table_element.' as t '; - $sql.= " WHERE 1"; + + if ($entityfilter) + { + if (! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) + { + if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) { + $sql.= " WHERE t.entity IS NOT NULL"; // Show all users + } else { + $sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug"; + $sql.= " WHERE ((ug.fk_user = t.rowid"; + $sql.= " AND ug.entity IN (".getEntity('user')."))"; + $sql.= " OR t.entity = 0)"; // Show always superadmin + } + } + else + { + $sql.= " WHERE t.entity IN (".getEntity('user').")"; + } + } + else + { + $sql.= " WHERE 1"; + } // Manage filter $sqlwhere = array(); diff --git a/htdocs/user/class/usergroup.class.php b/htdocs/user/class/usergroup.class.php index 50718ba82ec..eada58c3e3b 100644 --- a/htdocs/user/class/usergroup.class.php +++ b/htdocs/user/class/usergroup.class.php @@ -61,7 +61,7 @@ class UserGroup extends CommonObject /** * @var string * @deprecated - * @see name + * @see $name */ public $nom; diff --git a/htdocs/user/group/list.php b/htdocs/user/group/list.php index ce1caf8762a..0651e7aeff8 100644 --- a/htdocs/user/group/list.php +++ b/htdocs/user/group/list.php @@ -184,6 +184,7 @@ if ($resql) print_liste_field_titre("NbOfUsers", $_SERVER["PHP_SELF"], "nb", $param, "", '', $sortfield, $sortorder, 'center '); print_liste_field_titre("NbOfPermissions", $_SERVER["PHP_SELF"], "nbpermissions", $param, "", '', $sortfield, $sortorder, 'center '); print_liste_field_titre("DateCreationShort", $_SERVER["PHP_SELF"], "g.datec", $param, "", '', $sortfield, $sortorder, 'center '); + print_liste_field_titre("", $_SERVER["PHP_SELF"]); print "\n"; $grouptemp = new UserGroup($db); @@ -212,7 +213,8 @@ if ($resql) } print ''.$obj->nb.''; print ''.$obj->nbpermissions.''; - print ''.dol_print_date($db->jdate($obj->datec), "dayhour").''; + print ''.dol_print_date($db->jdate($obj->datec), "dayhour").''; + print ''; print "\n"; $i++; } diff --git a/htdocs/user/logout.php b/htdocs/user/logout.php index 9f462af9ad4..ae3fbfcbf8f 100644 --- a/htdocs/user/logout.php +++ b/htdocs/user/logout.php @@ -70,14 +70,6 @@ if (GETPOST('dol_no_mouse_hover')) $url.=(preg_match('/\?/', $url)?'&':'?' if (GETPOST('dol_use_jmobile')) $url.=(preg_match('/\?/', $url)?'&':'?').'dol_use_jmobile=1'; // Destroy session -/*$prefix=dol_getprefix(''); -$sessionname='DOLSESSID_'.$prefix; -$sessiontimeout='DOLSESSTIMEOUT_'.$prefix; -if (! empty($_COOKIE[$sessiontimeout])) ini_set('session.gc_maxlifetime',$_COOKIE[$sessiontimeout]); -session_name($sessionname); -session_destroy(); -dol_syslog("End of session ".$sessionname); -*/ dol_syslog("End of session ".session_id()); if (session_status() === PHP_SESSION_ACTIVE) { diff --git a/htdocs/variants/combinations.php b/htdocs/variants/combinations.php index 278d3656a6f..c29b2aae51e 100644 --- a/htdocs/variants/combinations.php +++ b/htdocs/variants/combinations.php @@ -711,7 +711,7 @@ if (! empty($id) || ! empty($ref)) 'presend'=>$langs->trans("SendByMail"), 'builddoc'=>$langs->trans("PDFMerge"), ); - if ($user->rights->product->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete"); + if ($user->rights->product->supprimer) $arrayofmassactions['predelete']=''.$langs->trans("Delete"); if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); */ diff --git a/htdocs/variants/generator.php b/htdocs/variants/generator.php index 3ab809028e9..b2b2b1bdfc6 100644 --- a/htdocs/variants/generator.php +++ b/htdocs/variants/generator.php @@ -41,6 +41,13 @@ $product = new Product($db); $product->fetch($id); +$error = 0; + + +/* + * Actions + */ + if (!$product->isProduct()) { header('Location: '.dol_buildpath('/product/card.php?id='.$product->id, 2)); exit(); @@ -59,8 +66,8 @@ $combinations = GETPOST('combinations', 'array'); $price_var_percent = (bool) GETPOST('price_var_percent'); $donotremove = true; -if ($_POST) { - +if ($_POST) +{ $donotremove = (bool) GETPOST('donotremove'); //We must check if all those given combinations actually exist @@ -102,7 +109,8 @@ if ($_POST) { $res = 1; - foreach (cartesianArray($adapted_values) as $currcomb) + $cartesianarray = cartesianArray($adapted_values); + foreach ($cartesianarray as $currcomb) { $res = $combination->createProductCombination($product, $currcomb, $sanitized_values, $price_var_percent); if ($res < 0) { diff --git a/htdocs/viewimage.php b/htdocs/viewimage.php index 7dec3cb7a0b..b25cc076836 100644 --- a/htdocs/viewimage.php +++ b/htdocs/viewimage.php @@ -169,6 +169,9 @@ $type = 'application/octet-stream'; if (GETPOST('type', 'alpha')) $type=GETPOST('type', 'alpha'); else $type=dol_mimetype($original_file); +// Security: This wrapper is for images. We do not allow type/html +if (preg_match('/html/', $type)) accessforbidden('Error: Using the image wrapper to output a file with a mime type HTML is not possible.', 1, 1, 1); + // Security: Delete string ../ into $original_file $original_file = str_replace("../", "/", $original_file); @@ -176,7 +179,7 @@ $original_file = str_replace("../", "/", $original_file); $refname=basename(dirname($original_file)."/"); // Security check -if (empty($modulepart)) accessforbidden('Bad value for parameter modulepart'); +if (empty($modulepart)) accessforbidden('Bad value for parameter modulepart', 1, 1, 1); $check_access = dol_check_secure_access_document($modulepart, $original_file, $entity, $refname); $accessallowed = $check_access['accessallowed']; diff --git a/htdocs/webservices/server_contact.php b/htdocs/webservices/server_contact.php index f612b3ce2d4..10dc97a7fb1 100644 --- a/htdocs/webservices/server_contact.php +++ b/htdocs/webservices/server_contact.php @@ -490,7 +490,7 @@ function getContactsForThirdParty($authentication, $idthirdparty) $sql = "SELECT c.rowid, c.fk_soc, c.civility as civility_id, c.lastname, c.firstname, c.statut as status,"; $sql.= " c.address, c.zip, c.town,"; $sql.= " c.fk_pays as country_id,"; - $sql.= " c.fk_departement,"; + $sql.= " c.fk_departement as state_id,"; $sql.= " c.birthday,"; $sql.= " c.poste, c.phone, c.phone_perso, c.phone_mobile, c.fax, c.email, c.jabberid,"; //$sql.= " c.priv, c.note, c.default_lang, c.canvas,"; @@ -503,7 +503,7 @@ function getContactsForThirdParty($authentication, $idthirdparty) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as d ON c.fk_departement = d.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."user as u ON c.rowid = u.fk_socpeople"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON c.fk_soc = s.rowid"; - $sql.= " WHERE c.fk_soc=$idthirdparty"; + $sql.= " WHERE c.fk_soc = ".$idthirdparty; $resql=$db->query($sql); if ($resql) diff --git a/htdocs/website/class/website.class.php b/htdocs/website/class/website.class.php index 6bbfb6ac472..a12bb2ebf11 100644 --- a/htdocs/website/class/website.class.php +++ b/htdocs/website/class/website.class.php @@ -519,7 +519,7 @@ class Website extends CommonObject */ public function createFromClone($user, $fromid, $newref, $newlang = '') { - global $conf, $hookmanager, $langs; + global $conf, $hookmanager; global $dolibarr_main_data_root; $now = dol_now(); diff --git a/htdocs/website/index.php b/htdocs/website/index.php index 123dfe759ab..2099102cced 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -210,6 +210,20 @@ $htmlheadercontentdefault.='-->'."\n"; * Actions */ +// Protections +if ($action == 'updatesource' && (GETPOST('refreshsite_x') || GETPOST('refreshsite.x') || GETPOST('refreshpage_x') || GETPOST('refreshpage.x'))) +{ + $action = 'preview'; // To avoid to update another page or another site when we click on button to select another site or page. +} +if (GETPOST('refreshsite', 'alpha')) // If we change the site, we reset the pageid and cancel addsite action. +{ + $pageid=0; + if ($action == 'addsite') $action = 'preview'; + if ($action == 'updatesource') $action = 'preview'; +} +if (GETPOST('refreshpage', 'alpha') && ! in_array($action, array('updatecss'))) $action='preview'; + + $backtopage=$_SERVER["PHP_SELF"].'?file_manager=1&website='.$websitekey.'&pageid='.$pageid.(GETPOST('section_dir', 'alpha')?'§ion_dir='.urlencode(GETPOST('section_dir', 'alpha')):''); // used after a confirm_deletefile into actions_linkedfiles.inc.php include DOL_DOCUMENT_ROOT.'/core/actions_linkedfiles.inc.php'; @@ -267,15 +281,6 @@ if ($action == 'adddir' && $permtouploadfile) } */ - -if (GETPOST('refreshsite', 'alpha')) // If we change the site, we reset the pageid and cancel addsite action. -{ - $pageid=0; - if ($action == 'addsite') $action = 'preview'; -} -if (GETPOST('refreshpage', 'alpha') && ! in_array($action, array('updatecss'))) $action='preview'; - - // Add site if ($action == 'addsite') { @@ -1443,8 +1448,40 @@ if (($action == 'updatesource' || $action == 'updatecontent' || $action == 'conf { $db->begin(); + $phpfullcodestringold = dolKeepOnlyPhpCode($objectpage->content); + $objectpage->content = GETPOST('PAGE_CONTENT', 'none'); + // Security analysis + $phpfullcodestring = dolKeepOnlyPhpCode($objectpage->content); + //print dol_escape_htmltag($phpfullcodestring);exit; + $forbiddenphpcommands=array("exec", "passthru", "system", "shell_exec", "proc_open"); + if (empty($conf->global->WEBSITE_PHP_ALLOW_WRITE)) // If option is not on, we disallow functions to write files + { + $forbiddenphpcommands=array_merge($forbiddenphpcommands, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "unlink", "mkdir", "rmdir", "symlink", "touch", "umask")); + } + foreach($forbiddenphpcommands as $forbiddenphpcommand) + { + if (preg_match('/'.$forbiddenphpcommand.'\s*\(/ms', $phpfullcodestring)) + { + $error++; + setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpcommand), null, 'errors'); + if ($action == 'updatesource') $action = 'editsource'; + if ($action == 'updatecontent') $action = 'editcontent'; + } + } + + if (empty($user->rights->website->writephp)) + { + if ($phpfullcodestringold != $phpfullcodestring) + { + $error++; + setEventMessages($langs->trans("NotAllowedToAddDynamicContent"), null, 'errors'); + if ($action == 'updatesource') $action = 'editsource'; + if ($action == 'updatecontent') $action = 'editcontent'; + } + } + // Clean data. We remove all the head section. $objectpage->content = preg_replace('/.*<\/head>/ims', '', $objectpage->content); /* $objectpage->content = preg_replace('//s', '', $objectpage->content); */ @@ -1455,6 +1492,8 @@ if (($action == 'updatesource' || $action == 'updatecontent' || $action == 'conf { $error++; setEventMessages($objectpage->error, $objectpage->errors, 'errors'); + if ($action == 'updatesource') $action = 'editsource'; + if ($action == 'updatecontent') $action = 'editcontent'; } if (! $error) @@ -1752,7 +1791,7 @@ if (! GETPOST('hide_websitemenu')) $out.=ajax_combobox('website'); print $out; //print ''; - print ''; + print ''; if ($websitekey) @@ -1845,7 +1884,7 @@ if (! GETPOST('hide_websitemenu')) } if (! empty($conf->global->WEBSITE_REPLACE_INFO_ABOUT_USAGE_WITH_WEBSERVER)) { - $htmltext.= '
'.$conf->global->WEBSITE_REPLACE_INFO_ABOUT_USAGE_WITH_WEBSERVER; + $htmltext.= '
'.$langs->trans($conf->global->WEBSITE_REPLACE_INFO_ABOUT_USAGE_WITH_WEBSERVER); } else { @@ -2917,11 +2956,12 @@ if ($action == 'preview' || $action == 'createfromclone' || $action == 'createpa // Change the contenteditable to "true" or "false" when mode Edit Inline is on or off if (empty($conf->global->WEBSITE_EDITINLINE)) { + // Remove the contenteditable="true" $newcontent = preg_replace('/(div|section)(\s[^\>]*)contenteditable="true"/', '\1\2', $newcontent); } else { - // TODO Add the contenteditable="true" when mode Edit Inline is on + // Keep the contenteditable="true" when mode Edit Inline is on } $out.=dolWebsiteReplacementOfLinks($object, $newcontent)."\n"; //$out.=$newcontent; diff --git a/htdocs/website/websiteaccount_card.php b/htdocs/website/websiteaccount_card.php index dfe189f2a74..5db74067ef6 100644 --- a/htdocs/website/websiteaccount_card.php +++ b/htdocs/website/websiteaccount_card.php @@ -63,11 +63,16 @@ if (empty($action) && empty($id) && empty($ref)) $action='view'; //if ($user->societe_id > 0) $socid = $user->societe_id; //$result = restrictedArea($user, 'website', $id); +$permissionnote=$user->rights->websiteaccount->write; // Used by the include of actions_setnotes.inc.php +$permissiondellink=$user->rights->websiteaccount->write; // Used by the include of actions_dellink.inc.php +$permissionedit=$user->rights->websiteaccount->write; // Used by the include of actions_lineupdown.inc.php +$permissiontoadd=$user->rights->websiteaccount->write; // Used by the include of actions_addupdatedelete.inc.php + // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); // Load object -include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals +include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once. diff --git a/test/phpunit/TicketTest.php b/test/phpunit/TicketTest.php index 4f3d06fc7cc..e5367d03fbe 100644 --- a/test/phpunit/TicketTest.php +++ b/test/phpunit/TicketTest.php @@ -349,7 +349,7 @@ class TicketTest extends PHPUnit_Framework_TestCase $langs=$this->savlangs; $db=$this->savdb; - $result=$localobject->close(); + $result=$localobject->close($user); print __METHOD__." id=".$localobject->id." result=".$result."\n"; $this->assertGreaterThan(0, $result);