diff --git a/.travis.yml b/.travis.yml index 22d4b2b0827..df6e24a7e02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,9 @@ dist: xenial language: php +git: + depth: 1 + # Start on every boot services: - memcached @@ -264,24 +267,27 @@ script: echo - | - echo "Checking PHP syntax errors" + echo "Checking PHP syntax errors (only 1 version to not overload travis and avoid duplicate tests)" # Ensure we catch errors set -e #parallel-lint --exclude htdocs/includes --blame . - parallel-lint --exclude dev/tools/test/namespacemig --exclude htdocs/includes/composer --exclude htdocs/includes/myclabs --exclude htdocs/includes/phpspec --exclude dev/initdata/dbf/includes \ - --exclude htdocs/includes/sabre --exclude htdocs/includes/phpoffice/PhpSpreadsheet --exclude htdocs/includes/sebastian \ - --exclude htdocs/includes/squizlabs/php_codesniffer --exclude htdocs/includes/jakub-onderka --exclude htdocs/includes/php-parallel-lint --exclude htdocs/includes/symfony \ - --exclude htdocs/includes/mike42/escpos-php/example --exclude htdocs/includes/maximebf \ - --exclude htdocs/includes/phpunit/ --exclude htdocs/includes/tecnickcom/tcpdf/include/barcodes --exclude htdocs/includes/webmozart --blame . + # Exclusions are defined in the ruleset.xml file + if [ "$TRAVIS_PHP_VERSION" = "7.4" ]; then + parallel-lint -e php --exclude dev/tools/test/namespacemig --exclude htdocs/includes/composer --exclude htdocs/includes/myclabs --exclude htdocs/includes/phpspec --exclude dev/initdata/dbf/includes \ + --exclude htdocs/includes/sabre --exclude htdocs/includes/phpoffice/PhpSpreadsheet --exclude htdocs/includes/sebastian \ + --exclude htdocs/includes/squizlabs/php_codesniffer --exclude htdocs/includes/jakub-onderka --exclude htdocs/includes/php-parallel-lint --exclude htdocs/includes/symfony \ + --exclude htdocs/includes/mike42/escpos-php/example --exclude htdocs/includes/maximebf \ + --exclude htdocs/includes/phpunit/ --exclude htdocs/includes/tecnickcom/tcpdf/include/barcodes --exclude htdocs/includes/webmozart --blame . + fi set +e echo - | - echo "Checking coding style (excluding Pull Requests builds to not overload travis, excluding also some jobs to avoid duplicate tests)" + echo "Checking coding style (only for Pull Requests builds and 1 version to not overload travis and avoid duplicate tests)" # Ensure we catch errors set -e # Exclusions are defined in the ruleset.xml file - if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_PHP_VERSION" = "7.4" ] && [ "$DB" = "mysql" ]; then + if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_PHP_VERSION" = "7.4" ]; then phpcs -s -p -d memory_limit=-1 --extensions=php --colors --tab-width=4 --standard=dev/setup/codesniffer/ruleset.xml --encoding=utf-8 --runtime-set ignore_warnings_on_exit true .; fi set +e @@ -401,8 +407,12 @@ script: php upgrade.php 13.0.0 14.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade13001400.log php upgrade2.php 13.0.0 14.0.0 > $TRAVIS_BUILD_DIR/upgrade13001400-2.log php step5.php 13.0.0 14.0.0 > $TRAVIS_BUILD_DIR/upgrade13001400-3.log + ls -alrt $TRAVIS_BUILD_DIR/ +- | + echo "Enabling new modules" # Enable modules not enabled into original dump + cd htdocs/install php upgrade2.php 0.0.0 0.0.0 MAIN_MODULE_API,MAIN_MODULE_PRODUCTBATCH,MAIN_MODULE_SUPPLIERPROPOSAL,MAIN_MODULE_STRIPE > $TRAVIS_BUILD_DIR/enablemodule.log php upgrade2.php 0.0.0 0.0.0 MAIN_MODULE_WEBSITE,MAIN_MODULE_TICKET,MAIN_MODULE_ACCOUNTING,MAIN_MODULE_MRP >> $TRAVIS_BUILD_DIR/enablemodule.log php upgrade2.php 0.0.0 0.0.0 MAIN_MODULE_RECEPTION,MAIN_MODULE_RECRUITMENT >> $TRAVIS_BUILD_DIR/enablemodule.log diff --git a/ChangeLog b/ChangeLog index 50a0ff10131..1fdb321822f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,13 +10,63 @@ For users: NEW: Module Recruitement to follow application to job positions is now stable. NEW: Feature to make Stock Inventories NEW: Several security issues after a second private bug hunting campaign. -NEW: Add a security center page with all information and advices related to the security of your instance -NEW: Add a performance center page with all information and advices related to the performance of your instance NEW: A lot of fix into english text after a small proofreading campaign (still not perfect, but really better) NEW: All main menu entries are using the picto of the module NEW: Add a copy to clipboard button on some fields -NEW: Add an example of scheduled job to send email reminder for unpayed invoices -NEW: Can make massive stock transfers from a CSV file. +NEW: Add an example of scheduled job to send email reminder for unpaid invoices +NEW: Add some color and picto for the direction of movement +NEW: add the column "Channel" into the list of orders +NEW: Add the column "alias" of company in the list of proposal, order, invoice +NEW: Add the column "Office phone" and "User mobile" in user list +NEW: Add the column "Price level" in thirdparty list +NEW: Add some company information in the dropdown login menu +NEW: Add edit/delete action icons on categories list pages +NEW: Add hidden option to auto load input line extrafield into new lines +NEW: Add import profile to import BOM +NEW: Add link picto to the stock movement on the detail of production +NEW: Add mass action "Set tag" for product/service, user, thirdparty, warehouse, project, bank account, members +NEW: Add bulk action Validate and Set to billed on order list. +NEW: Add possibility to exports userGroups fields on user exports +NEW: Can search on lots or serials into the quick search bar +NEW: Add support for Friday as a non working day +NEW: auto notification with module Notification can use templated emails +NEW: Can clone a cron job +NEW: Can create a lot from the list view (Look and feel standardization). +NEW: Can filter on extrafields date on lists +NEW: Can filter on rowid in list of blocked logs +NEW: Can hide columns "time consumed" on timesheet per week +NEW: Can set an employee on each social contribution +NEW: Can set order of execution of hooks +NEW: Can toggle FCKeditor on public/private notes +NEW: Check update availability for externals modules using a button on module page +NEW: Choose lines to use while creating intervention card from origin +NEW: Columns shipment method, payment mode, payment term in proposal and order list +NEW: Conf for default actioncomm status +NEW: Dictionary for availability - Add a column position +NEW: Can set a user related to a social contribution +NEW: ICS Direct debit can be set with a different value for each bank account +NEW: LDAP: usergroup search can be filtered +NEW: Make public bookmarks editable by admin users only +NEW: If main logo not defined, can use the squarred logo on login page +NEW: The manifest file can use the squared image if available +NEW: Enhance the multicurrency rate editor +NEW: Normalyse Type company field with ajax combobox +NEW: preload product description on selection for customer propal/order/invoice +NEW: Search usergroups & resources +NEW: Setup Page for module creation with module builder enhancement #FoundationFunding +NEW: Show picto of module into the list of dictionaries +NEW: Show the total of payment on the payment confirmation page +NEW: The global setup for Mandatory fields can now be done on combo list too. +NEW: translate in "en_US" to complete PR 16980 +NEW: Update the list of taxes available by default for France +NEW: Salary payment request and Salary payment are 2 different steps in workflow on Salary payment recording +NEW: VAT payment request and VAT payment are now 2 different steps in workflow on VAT payment recording +NEW: VAT report - Optimisation & collapse by rate +NEW: When a doc file is shared, link is visible from the main page of doc. +NEW: #16378 more E-Mail Contact substitution Values for better salutation +NEW: option to keep the "Automatically create a total payment" checkbox empty on the tax creation page + + Accountancy NEW: Accountancy - Add FEC import NEW: Accountancy - Add a confirmation form with options on export NEW: Accountancy - Add select date from/to in already bind customer and supplier list @@ -24,152 +74,158 @@ NEW: Accountancy - Format FEC - Add new field DateLimitReglmt NEW: Accountancy - In ledger & journals, show link on bank transaction NEW: Accountancy - Possibility to filter on journals in balance NEW: Accountancy - Add a page to list subledger accounts -NEW: add the Channel column into the list of orders -NEW: Add a check to avoid an invoice date in the future -NEW: Add some color and picto for the direction of movement -NEW: Add the column "alias" of company in the list of proposal, order, invoice -NEW: Add the column "Office phone" and "User mobile" in user list -NEW: Add the column "Price level"in thirdparty list -NEW: Add some company information in the dropdown login menu -NEW: Add constant MAIN_BUGTRACK_URL to set a custom url to redirect to when clicking on link "declare a bug" -NEW: Add contact tag and bulk email status on the thirparty+contact create form -NEW: Add db fields note_public and note_private for ECM module -NEW: Manage deposit on supplier invoice (& somes ajustments) -NEW: Add edit/delete action icons on categories list pages -NEW: Add hidden option to auto load input line extrafield into new lines -NEW: Add import profile to import BOM -NEW: Add last date of modification for website pages in the list of pages -NEW: Add link picto to the stock movement on the detail of production -NEW: Add mass action "Set tag" for product/service, user, thirdparty, warehouse, project, bank account, members -NEW: Add bulk action Validate and Set to billed on order list. -NEW: add bulk action to set a commercial proposal to status "Refused" -NEW: Add option CONTRACT_ALLOW_EXTERNAL_DOWNLOAD and SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD to make generated doc automatically shared. -NEW: Add option MAIN_SECURITY_ANTI_SSRF_SERVER_IP to define list of IPs that are local IPs -NEW: Add option SOCIETE_DISABLE_WORKFORCE to hide staff field -NEW: Add a new permission "Export website" -NEW: Add possibility to exports userGroups fields on user exports -NEW: Add price min and price min including tax into product export -NEW: Can search on lots or serials into the quick search bar -NEW: Add support for Friday as a non working day -NEW: Add a total in page of margin by user -NEW: Add the total of margin in invoice list -NEW: Can set a Warehouse on a Thirparty -NEW: auto notification with module Notification can use templated emails -NEW: Bank Entries : display user linked to a salary or a taxes -NEW: Can set a warehouse in a proposal -NEW: Can clone a cron job. -NEW: Can create a lot from the list view (Look and feel standardization). -NEW: Can filter files in GED on status Shared/Not shared -NEW: Can filter on extrafields date on lists -NEW: Can filter on rowid in list of blocked logs. -NEW: Can hide columns "time consumed" on timesheet per week. -NEW: Can set an employee on each social contribution -NEW: Can set a percentage when creating an invoice from another object -NEW: Can set if a ticket group is visible on public interface or not. -NEW: Can set order of execution of hooks -NEW: Can show the leave dates/holidays on the agenda view. -NEW: Can show the project ref into PDF documents -NEW: Can toggle FCKeditor on public/private notes -NEW: Can use captcha on public page to create a ticket -NEW: Check update availability for externals modules using a button on module page -NEW: Choose lines to use while creating intervention card from origin -NEW: Column shippement method, payment mode, payment term in proposal and order list -NEW: Conf for default actiomm status -NEW: customer ref for product customer prices -NEW: date and user signature on proposal (Issue 16062) -NEW: Dictionary for availability - Add a column position -NEW: TAKEPOS delayed payment in TakePOS -NEW: TAKEPOS display date range if exist in TakePOS -NEW: TAKEPOS display resiliate status in TakePOS for member -NEW: TAKEPOS Edit sales lines rights in TakePOS -NEW: TAKEPOS Option for Takepos to show the total price without tax -NEW: TAKEPOS More permission in TakePOS (Can edit added line, can modify once order sent to kitchen) -NEW: Can set a user related to a social contribution -NEW: ICS Direct debit can be set with a different value for each bank account -NEW: LDAP: usergroup search can be filtered -NEW: Make public bookmarks editable by admin users only -NEW: If main logo not defined, can use the squarred logo on login page -NEW: The manifest file can use the squared image if available -NEW: Add bulk actions for Bank Transfer -NEW: Enhance the multicurrency rate editor NEW: Multiselect ledger account code filter on book keeping list -NEW: Normalyse Type company field with ajax combobox -NEW: option to automatically close an open project when all its tasks are done (=progress 100%) -NEW: option to select membership type on the online payment page for membership subscription or renewal -NEW: preload product description on selection for customer propal/order/invoice -NEW: Add a ref in product customer price -NEW: Save old page with .old extension on disk when editing a website page -NEW: Search usergroups & resources -NEW: Set status of all variants when changing status of parent -NEW: Setup Page for module creation with module builder enhancement #FoundationFunding -NEW: Show picto of module into the list of dictionaries -NEW: Show the total of payment on the payment confirmation page -NEW: Stock movement list - Add more complete date field + + Agenda +NEW: add support for Friday as a non working day +NEW: can show the leave dates/holidays on the agenda view NEW: Support color for types of event -NEW: The global setup for Mandatory fields can now be done on combo list too. -NEW: translate in "en_US" to complete PR 16980 -NEW: Update the list of taxes available by default for France -NEW: Add captcha on public page to create a ticket -NEW: Salary payment request and Salary payment are 2 different steps in workflow on Salary payment recording -NEW: VAT payment request and VAT payment are now 2 different steps in workflow on VAT payment recording -NEW: VAT report - Optimisation & collapse by rate -NEW: When we add contacts/users to a project, ask to also affect them on tasks. -NEW: When a doc file is shared, link is visible from the main page of doc. -NEW: Add option in Workflow module to set a shipment as closed. -NEW: Option to automatically create a login/user when a new subscription of a member is done online -NEW: #16378 More E-Mail Contact substitution Values for better salutation -NEW: option to keep the "Automatically create a total payment" checkbox empty on the tax creation page -NEW: #17113 Can upload a favicon in website module + + Bank +NEW: Bank Entries : display user linked to a salary or a taxes +NEW: Add bulk actions for Bank Transfer + + ECM/GED +NEW: add DB fields note_public and note_private for ECM module +NEW: can filter files in ECM/GED on status Shared / Not shared + + Invoices +NEW: add a check to avoid an invoice date in the future +NEW: add the total of margin in invoice list +NEW: can set a percentage when creating an invoice from another object +NEW: Support down payment on supplier invoice (& somes ajustments) + + Margin +NEW: add the total of margin in invoice list +NEW: add the total of margin in page by user + + Members NEW: #17292 default subscription amount by adherent type +NEW: option to automatically create a login/user when a new subscription of a member is done online +NEW: option to select membership type on the online payment page for membership subscription or renewal + + Products +NEW: Add price min and price min including tax into product export +NEW: Add a ref in product customer price +NEW: customer ref for product customer prices +NEW: Set status of all variants when changing status of parent + + Projects/Tasks +NEW: option to automatically close an open project when all its tasks are done (=progress 100%) +NEW: can show the project ref into PDF documents +NEW: when we add contacts/users to a project, ask to also affect them on tasks +NEW: Can hide columns "time consumed" on timesheet per week + + Proposals +NEW: add bulk action to set a commercial proposal to status "Refused" +NEW: can set a warehouse in a proposal +NEW: date and user signature on proposal (Issue 16062) #16980 + + Shipment +NEW: add option in Workflow module to set a shipment as closed + + TakePOS +NEW: delayed payment in TakePOS #14456? +NEW: display date range if exist in TakePOS +NEW: display resiliate status in TakePOS for member +NEW: edit sales lines rights in TakePOS +NEW: option for TakePOS to show the total price without tax +NEW: more permissions in TakePOS (can edit added line, can modify once order sent to kitchen) + + Third Party / Contacts +NEW: add contact tag and bulk email status on the thirdparty + contact create form +NEW: can set a warehouse on a thirdparty + + Tickets +NEW: can use captcha on public page to create a ticket #16347 +NEW: can set if a ticket group is visible on public interface or not + + Warehouse/Stock +NEW: Feature to make Stock Inventories +NEW: can make massive stock transfers from a CSV file +NEW: Stock movement list - add more complete date field +NEW: can set a warehouse in a proposal +NEW: can set a warehouse on a thirdparty + + Website Module +NEW: #17113 Can upload a favicon in website module +NEW: add a new permission "Export website" +NEW: add last date of modification for website pages in the list of pages +NEW: Save old page with .old extension on disk when editing a website page + + Workflows +NEW: add option in Workflow module to set a shipment as closed + + + for Admins +NEW: Add a security center page with all information and advices related to the security of your instance +NEW: Add a performance center page with all information and advices related to the performance of your instance + + Modules +NEW: Module Recruitement is now stable NEW: start new experimental module Event Organization Management NEW: start new experimental module Partnership Management NEW: start new experimental module Knowledge Management -NEW: start new experimental module Workstations management +NEW: start new experimental module Workstations Management +NEW: Check update availability for externals modules using a button on module page +Module SimplePOS is deprecated - TakePOS is recommended for the future + + new Options +NEW: add option CONTRACT_ALLOW_EXTERNAL_DOWNLOAD to make generated doc automatically shared +NEW: add option SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD to make generated doc automatically shared +NEW: add option MAIN_SECURITY_ANTI_SSRF_SERVER_IP to define list of IPs that are local IPs +NEW: add option SOCIETE_DISABLE_WORKFORCE to hide staff field +NEW: add constant MAIN_BUGTRACK_URL to set a custom url to redirect to when clicking on link "declare a bug" For developers: --------------- NEW: Can edit an object property in module builder -NEW: hook printFieldListTitle for cabyprodserv.php -NEW: Hook to allow external modules to add their own shortlist of recent objects -NEW: SQL-altering hooks in the turnover by product report NEW: Add data-eec=1 for EEC countries on select for js interaction NEW: Add experimental repair script to switch to dynamic row format and utf8mb4 encoding -NEW: add form confirm hook on company card NEW: Add function showValueWithClipboardCPButton() to add a copy/paste -NEW: Add hook addSectionECMAuto method to add custom diretory into ECM auto files -NEW: Add native compression in rest apis -NEW: Product Variants API, add variant stock to response by parameter NEW: Upgrade Stripe PHP lib to 7.67.0 -NEW: Add link to OpenAPI specifications xml file in REST API module setup: swagger.json file can be included into external tools like redoc NEW: Support sepa_debit in stripe paymentmethods list NEW: Update doleditor.class.php for easily activate SCAYT NEW: Add triggers in the function add_object_linked(), updateObjectLinked() and deleteObjectLinked() NEW: Add triggers OBJECT_LINK_INSERT, OBJECT_LINK_UPDATE et OBJECT_LINK_DELETE in the function add_object_linked(), updateObjectLinked() and deleteObjectLinked() -NEW: API Add option $includeifobjectisused to get a product -NEW: API Get the list of product ids only -NEW: Can set a target image in dolcropresize function. -NEW: Can set a label as placeholder for combo lists. +NEW: can set a target image in dolcropresize function +NEW: can set a label as placeholder for combo lists NEW: Add pagination on Get Products response API NEW: Add the DefaultValues CRUD class NEW: Extrafields of documents lines are inside the lines, not any more on separate TR NEW: unit selection on object edit line -NEW: #13739 #17390 Product API route added to get product stock and product with or without variants + + APIs +NEW: API add option $includeifobjectisused to get a product +NEW: API get the list of product ids only +NEW: add link to OpenAPI specifications XML file in REST API module setup: swagger.json file can be included into external tools like redoc +NEW: add native compression in REST APIs +NEW: Product Variants API, add variant stock to response by parameter +NEW: Product API route added to get product stock and product with or without variants #13739 #17390 + + Hooks +NEW: hook printFieldListTitle for cabyprodserv.php +NEW: hook to allow external modules to add their own shortlist of recent objects +NEW: SQL-altering hooks in the turnover by product report +NEW: add form confirm hook on company card +NEW: add hook addSectionECMAuto method to add custom diretory into ECM auto files WARNING: Following changes may create regressions for some external modules, but were necessary to make Dolibarr better: * Module SimplePOS is deprecated. If you need a POS module, please use the module TakePOS. -* The ICS value for direct debit or credit transfer is now store on each bank account instead of into the global setup. +* The ICS value for direct debit or credit transfer is now stored on each bank account instead of into the global setup. * API /setup/shipment_methods has been replaced with API /setup/shipping_methods -* Field "total" renamed into to "total_ht" for table llx_facture, llx_facture_rec for better field name consistency -* Field "tva" renamed into "total_tva" for table llx_propal, llx_supplier_proposal, llx_commande, llx_commande_fournisseur for better field name consistency -* Field "total" renamed into "total_ttc" for table lx_propal, llx_supplier_proposal for better field name consistency -* If your database is PostgreSql, you must use version 9.1.0 or more (Dolibarr need the SQL function CONCAT) -* If your database is MySql or MariaDB, you need at least version 5.1 +* Field "total" renamed into "total_ht" for table llx_facture, llx_facture_rec for better field name consistency +* Field "tva" renamed into "total_tva" for table llx_propal, llx_supplier_proposal, llx_commande, llx_commande_fournisseur for better field name consistency +* Field "total" renamed into "total_ttc" for table llx_propal, llx_supplier_proposal for better field name consistency +* If your database is PostgreSQL, you must use version 9.1.0 or more (Dolibarr need the SQL function CONCAT) +* If your database is MySQL or MariaDB, you need at least version 5.1 * Function set_price_level() has been renamed into setPriceLevel() to follow camelcase rules -* Remove deprecated subtituion key __REFCLIENT__ (Replaced with __REF_CLIENT__) +* removed deprecated subtituion key __REFCLIENT__ (replaced with __REF_CLIENT__) + ***** ChangeLog for 13.0.3 compared to 13.0.2 ***** @@ -180,6 +236,8 @@ FIX: #17060 FIX: #17192 - With tz < 0, event is show in bad day on calendar views FIX: #17363 FIX: #17476 releve.php: Fix SQL statement +FIX: #17967 +Fix: #17906 : fix access denied FIX: Accountancy - Import in general ledger FIX: Accountancy - Quadra export - wrong data on credit FIX: Accountancy - Warning on the pages of the preparatory statements of accounting entries @@ -193,7 +251,7 @@ FIX: create sociales : keep values error form FIX: dol_print_date for %a and %b with some timezone FIX: email is not case sensitive FIX: error for duplicate thirdparty found correctly returned by ws -FIX: Espadon PDF shippment model with long public note now wroking +FIX: Espadon PDF shippment model with long public note now working FIX: esupplier order: error 500 when using packaging with product where it is not defined FIX: Filter on debit/credit FIX: Filter on supplier payment list diff --git a/README.md b/README.md index 34162fd5682..7878f6270a7 100644 --- a/README.md +++ b/README.md @@ -97,49 +97,80 @@ See the [ChangeLog](https://github.com/Dolibarr/dolibarr/blob/develop/ChangeLog) ### Main application/modules (all optional) -- Customers, Prospects (Leads) and/or Suppliers directory + Contacts -- Members/Membership management -- Products and/or Services catalog -- Commercial proposals management -- Customer & Supplier Orders management -- Invoices and payment management -- Shipping management -- Warehouse/Stock management/Inventory -- Manufacturing Orders -- Bank accounts management -- Direct debit orders management (European SEPA) -- Accounting management -- Shared calendar/agenda (with ical and vcal export for third party tools integration) -- Opportunities or Leads management -- Projects & Tasks management -- Ticket System -- Contracts management -- Interventions management -- Employee's leave requests management -- Expense reports -- Recruitment management -- Timesheets -- Electronic Document Management (EDM) -- Foundations members management -- Point of Sale (POS) -- … (around 100 modules available by default, 1000+ on the addon market place) +- Third-Parties Management: Customers, Prospects (Leads) and/or Suppliers + Contacts +- Members/Membership/Foundation management + + Product Management +- Products and/or Services catalog +- Stock / Warehouse management + Inventory +- Barcodes +- Batches / Lots / Serials +- Product Variants +- Bill of Materials (BOM) +- Manufacturing Orders + + Customer/Sales Management +- Customers/Prospects + Contacts management +- Opportunities or Leads management +- Commercial proposals management +- Customer Orders management +- Contracts/Subscription management +- Interventions management +- Ticket System +- Shipping management +- Customer Invoices/Credit notes and payment management +- Point of Sale (POS) + + Supplier/Purchase Management +- Suppliers/Vendors + Contacts +- Supplier (price) requests +- Purchase Orders management +- Delivery/Receiption +- Supplier Invoices/credit notes and payment management +- INCOTERMS + + Finance / Accounting +- Invoices / Payments +- Bank accounts management +- Direct debit orders management (European SEPA) +- Accounting management +- Donations management +- Loan management +- Margins +- Reports + + Collaboration +- Shared calendar/agenda (with ical and vcal export for third party tools integration) +- Projects & Tasks management +- Ticket System +- Surveys + + HR +- Employee's leave requests management +- Expense reports +- Recruitment management +- Timesheets + ### Other application/modules +- Electronic Document Management (EDM) - Bookmarks management -- Donations management - Reporting -- Surveys - Data export/import -- Barcodes support +- Barcodes - Margin calculations - LDAP connectivity - ClickToDial integration - Mass emailing - RSS integration - Skype integration +- Social platforms linking - Payment platforms integration (PayPal, Stripe, Paybox...) -- … +- Email-Collector + +(around 100 modules available by default, 1000+ on the addon market place) + ### Other general features @@ -171,9 +202,12 @@ See the [ChangeLog](https://github.com/Dolibarr/dolibarr/blob/develop/ChangeLog) ### System Environment / Requirements -- Works with PHP 5.6+ and MariaDB 5.0.3+, MySQL 5.0.3+ or PostgreSQL 8.1.4+ (See requirements on the [Wiki](https://wiki.dolibarr.org/index.php/Prerequisite)) +- PHP +- MariaDB, MySQL or PostgreSQL - Compatible with all Cloud solutions that match PHP & MySQL or PostgreSQL prerequisites. +See exact requirements on the [Wiki](https://wiki.dolibarr.org/index.php/Prerequisite) + ### Extending diff --git a/dev/examples/zapier/creates/contact.js b/dev/examples/zapier/creates/contact.js new file mode 100644 index 00000000000..bcb849ad63d --- /dev/null +++ b/dev/examples/zapier/creates/contact.js @@ -0,0 +1,74 @@ +/*jshint esversion: 6 */ +// create a particular contact by name +const createContact = async (z, bundle) => { + const apiurl = bundle.authData.url + '/api/index.php/contacts'; + + const response = await z.request({ + method: 'POST', + url: apiurl, + body: { + name: bundle.inputData.name, + name_alias: bundle.inputData.name_alias, + ref_ext: bundle.inputData.ref_ext, + ref_int: bundle.inputData.ref_int, + address: bundle.inputData.address, + zip: bundle.inputData.zip, + town: bundle.inputData.town, + country_code: bundle.inputData.country_code, + country_id: bundle.inputData.country_id, + country: bundle.inputData.country, + phone: bundle.inputData.phone, + email: bundle.inputData.email, + sens: 'fromzapier' + } + }); + const result = z.JSON.parse(response.content); + // api returns an integer when ok, a json when ko + return result.response || {id: response}; +}; + +module.exports = { + key: 'contact', + noun: 'Contact', + + display: { + label: 'Create Contact', + description: 'Creates a contact.' + }, + + operation: { + inputFields: [ + {key: 'name', required: true}, + {key: 'name_alias', required: false}, + {key: 'address', required: false}, + {key: 'zip', required: false}, + {key: 'town', required: false}, + {key: 'email', required: false} + ], + perform: createContact, + + sample: { + id: 1, + name: 'DUPOND', + name_alias: 'DUPOND Ltd', + address: 'Rue des Canaries', + zip: '34090', + town: 'MONTPELLIER', + phone: '0123456789', + fax: '2345678901', + email: 'robot@domain.com' + }, + + outputFields: [ + {key: 'id', type: "integer", label: 'ID'}, + {key: 'name', label: 'Name'}, + {key: 'name_alias', label: 'Name alias'}, + {key: 'address', label: 'Address'}, + {key: 'zip', label: 'Zip'}, + {key: 'town', label: 'Town'}, + {key: 'phone', label: 'Phone'}, + {key: 'fax', label: 'Fax'}, + {key: 'email', label: 'Email'} + ] + } +}; diff --git a/dev/examples/zapier/creates/member.js b/dev/examples/zapier/creates/member.js new file mode 100644 index 00000000000..152f1129e79 --- /dev/null +++ b/dev/examples/zapier/creates/member.js @@ -0,0 +1,74 @@ +/*jshint esversion: 6 */ +// create a particular member by name +const createMember = async (z, bundle) => { + const apiurl = bundle.authData.url + '/api/index.php/members'; + + const response = await z.request({ + method: 'POST', + url: apiurl, + body: { + name: bundle.inputData.name, + name_alias: bundle.inputData.name_alias, + ref_ext: bundle.inputData.ref_ext, + ref_int: bundle.inputData.ref_int, + address: bundle.inputData.address, + zip: bundle.inputData.zip, + town: bundle.inputData.town, + country_code: bundle.inputData.country_code, + country_id: bundle.inputData.country_id, + country: bundle.inputData.country, + phone: bundle.inputData.phone, + email: bundle.inputData.email, + sens: 'fromzapier' + } + }); + const result = z.JSON.parse(response.content); + // api returns an integer when ok, a json when ko + return result.response || {id: response}; +}; + +module.exports = { + key: 'member', + noun: 'Member', + + display: { + label: 'Create Member', + description: 'Creates a member.' + }, + + operation: { + inputFields: [ + {key: 'name', required: true}, + {key: 'name_alias', required: false}, + {key: 'address', required: false}, + {key: 'zip', required: false}, + {key: 'town', required: false}, + {key: 'email', required: false} + ], + perform: createMember, + + sample: { + id: 1, + name: 'DUPOND', + name_alias: 'DUPOND Ltd', + address: 'Rue des Canaries', + zip: '34090', + town: 'MONTPELLIER', + phone: '0123456789', + fax: '2345678901', + email: 'robot@domain.com' + }, + + outputFields: [ + {key: 'id', type: "integer", label: 'ID'}, + {key: 'name', label: 'Name'}, + {key: 'name_alias', label: 'Name alias'}, + {key: 'address', label: 'Address'}, + {key: 'zip', label: 'Zip'}, + {key: 'town', label: 'Town'}, + {key: 'phone', label: 'Phone'}, + {key: 'fax', label: 'Fax'}, + {key: 'email', label: 'Email'} + ] + } +}; diff --git a/dev/examples/zapier/index.js b/dev/examples/zapier/index.js index d1897673b39..fdd1ed29a53 100644 --- a/dev/examples/zapier/index.js +++ b/dev/examples/zapier/index.js @@ -2,12 +2,18 @@ const triggerAction = require('./triggers/action'); const triggerOrder = require('./triggers/order'); const triggerThirdparty = require('./triggers/thirdparty'); +const triggerContact = require('./triggers/contact'); const triggerTicket = require('./triggers/ticket'); const triggerUser = require('./triggers/user'); +const triggerMember = require('./triggers/member'); const searchThirdparty = require('./searches/thirdparty'); +const searchContact = require('./searches/contact'); +const searchMember = require('./searches/member'); const createThirdparty = require('./creates/thirdparty'); +const createContact = require('./creates/contact'); +const createMember = require('./creates/member'); const { config: authentication, @@ -62,18 +68,24 @@ const App = { [triggerAction.key]: triggerAction, [triggerOrder.key]: triggerOrder, [triggerThirdparty.key]: triggerThirdparty, + [triggerContact.key]: triggerContact, [triggerTicket.key]: triggerTicket, [triggerUser.key]: triggerUser, + [triggerMember.key]: triggerMember, }, // If you want your searches to show up, you better include it here! searches: { [searchThirdparty.key]: searchThirdparty, + [searchContact.key]: searchContact, + [searchMember.key]: searchMember, }, // If you want your creates to show up, you better include it here! creates: { [createThirdparty.key]: createThirdparty, + [createContact.key]: createContact, + [createMember.key]: createMember, } }; diff --git a/dev/examples/zapier/package.json b/dev/examples/zapier/package.json index 4d5c5daa867..a9d519dec69 100644 --- a/dev/examples/zapier/package.json +++ b/dev/examples/zapier/package.json @@ -1,6 +1,6 @@ { "name": "dolibarr", - "version": "1.13.0", + "version": "1.14.0", "description": "An app for connecting Dolibarr to the Zapier platform.", "repository": "Dolibarr/dolibarr", "homepage": "https://www.dolibarr.org/", @@ -11,7 +11,7 @@ "test": "mocha --recursive" }, "engines": { - "node": "8.10.0", + "node": "14.0.0", "npm": ">=5.6.0" }, "dependencies": { diff --git a/dev/examples/zapier/searches/contact.js b/dev/examples/zapier/searches/contact.js new file mode 100644 index 00000000000..b52b8d3e367 --- /dev/null +++ b/dev/examples/zapier/searches/contact.js @@ -0,0 +1,95 @@ +module.exports = { + key: 'contact', + + // You'll want to provide some helpful display labels and descriptions + // for users. Zapier will put them into the UX. + noun: 'Contact', + display: { + label: 'Find a Contact', + description: 'Search for contact.' + }, + + // `operation` is where we make the call to your API to do the search + operation: { + // This search only has one search field. Your searches might have just one, or many + // search fields. + inputFields: [ + { + key: 'lastname', + type: 'string', + label: 'Lastname', + helpText: 'Lastname to limit to the search to (i.e. The company or %company%).' + }, + { + key: 'email', + type: 'string', + label: 'Email', + helpText: 'Email to limit to the search to.' + } + ], + + perform: async (z, bundle) => { + const url = bundle.authData.url + '/api/index.php/contacts/'; + + // Put the search value in a query param. The details of how to build + // a search URL will depend on how your API works. + let filter = ''; + if (bundle.inputData.lastname) { + filter = "t.lastname like \'%"+bundle.inputData.name+"%\'"; + } + if (bundle.inputData.email) { + if (bundle.inputData.lastname) { + filter += " and "; + } + filter += "t.email like \'"+bundle.inputData.email+"\'"; + } + const response = await z.request({ + url: url, + // this parameter avoid throwing errors and let us manage them + skipThrowForStatus: true, + params: { + sqlfilters: filter + } + }); + //z.console.log(response); + if (response.status != 200) { + return []; + } + return response.json; + }, + + // In cases where Zapier needs to show an example record to the user, but we are unable to get a live example + // from the API, Zapier will fallback to this hard-coded sample. It should reflect the data structure of + // returned records, and have obviously dummy values that we can show to any user. + sample: { + id: 1, + createdAt: 1472069465, + name: 'DOE', + firstname: 'John', + authorId: 1, + directions: '1. Boil Noodles\n2.Serve with sauce', + style: 'italian' + }, + + // If the resource can have fields that are custom on a per-user basis, define a function to fetch the custom + // field definitions. The result will be used to augment the sample. + // outputFields: () => { return []; } + // Alternatively, a static field definition should be provided, to specify labels for the fields + outputFields: [ + { + key: 'id', + type: "integer", + label: 'ID' + }, + {key: 'createdAt', type: "integer", label: 'Created At'}, + {key: 'name', label: 'Name'}, + {key: 'firstname', label: 'Firstname'}, + {key: 'directions', label: 'Directions'}, + {key: 'authorId', type: "integer", label: 'Author ID'}, + { + key: 'style', + label: 'Style' + } + ] + } +}; diff --git a/dev/examples/zapier/searches/member.js b/dev/examples/zapier/searches/member.js new file mode 100644 index 00000000000..f1a84061146 --- /dev/null +++ b/dev/examples/zapier/searches/member.js @@ -0,0 +1,88 @@ +module.exports = { + key: 'member', + + // You'll want to provide some helpful display labels and descriptions + // for users. Zapier will put them into the UX. + noun: 'Member', + display: { + label: 'Find a Member', + description: 'Search for member.' + }, + + // `operation` is where we make the call to your API to do the search + operation: { + // This search only has one search field. Your searches might have just one, or many + // search fields. + inputFields: [ + { + key: 'lastname', + type: 'string', + label: 'Lastname', + helpText: 'Lastname to limit to the search to (i.e. The company or %company%).' + }, + { + key: 'email', + type: 'string', + label: 'Email', + helpText: 'Email to limit to the search to.' + } + ], + + perform: async (z, bundle) => { + const url = bundle.authData.url + '/api/index.php/members/'; + + // Put the search value in a query param. The details of how to build + // a search URL will depend on how your API works. + let filter = ''; + if (bundle.inputData.lastname) { + filter = "t.lastname like \'%" + bundle.inputData.name + "%\'"; + } + if (bundle.inputData.email) { + if (bundle.inputData.lastname) { + filter += " and "; + } + filter += "t.email like \'" + bundle.inputData.email + "\'"; + } + const response = await z.request({ + url: url, + // this parameter avoid throwing errors and let us manage them + skipThrowForStatus: true, + params: { + sqlfilters: filter + } + }); + //z.console.log(response); + if (response.status != 200) { + return []; + } + return response.json; + }, + + // In cases where Zapier needs to show an example record to the user, but we are unable to get a live example + // from the API, Zapier will fallback to this hard-coded sample. It should reflect the data structure of + // returned records, and have obviously dummy values that we can show to any user. + sample: { + id: 1, + createdAt: 1472069465, + name: 'DOE', + firstname: 'John', + authorId: 1, + }, + + // If the resource can have fields that are custom on a per-user basis, define a function to fetch the custom + // field definitions. The result will be used to augment the sample. + // outputFields: () => { return []; } + // Alternatively, a static field definition should be provided, to specify labels for the fields + outputFields: [ + { + key: 'id', + type: "integer", + label: 'ID' + }, + { key: 'createdAt', type: "integer", label: 'Created At' }, + { key: 'name', label: 'Name' }, + { key: 'firstname', label: 'Firstname' }, + { key: 'authorId', type: "integer", label: 'Author ID' }, + ] + } +}; diff --git a/dev/examples/zapier/searches/thirdparty.js b/dev/examples/zapier/searches/thirdparty.js index 8f72b9270e5..e1e6878f5b3 100644 --- a/dev/examples/zapier/searches/thirdparty.js +++ b/dev/examples/zapier/searches/thirdparty.js @@ -19,21 +19,43 @@ module.exports = { type: 'string', label: 'Name', helpText: 'Name to limit to the search to (i.e. The company or %company%).' + }, + { + key: 'email', + type: 'string', + label: 'Email', + helpText: 'Email to limit to the search to.' } ], - perform: (z, bundle) => { + perform: async (z, bundle) => { const url = bundle.authData.url + '/api/index.php/thirdparties/'; // Put the search value in a query param. The details of how to build // a search URL will depend on how your API works. - const options = { - params: { - sqlfilters: "t.nom like \'%"+bundle.inputData.name+"%\'" + let filter = ''; + if (bundle.inputData.name) { + filter = "t.nom like \'%"+bundle.inputData.name+"%\'"; + } + if (bundle.inputData.email) { + if (bundle.inputData.name) { + filter += " and "; } - }; - - return z.request(url, options).then(response => JSON.parse(response.content)); + filter += "t.email like \'"+bundle.inputData.email+"\'"; + } + const response = await z.request({ + url: url, + // this parameter avoid throwing errors and let us manage them + skipThrowForStatus: true, + params: { + sqlfilters: filter + } + }); + //z.console.log(response); + if (response.status != 200) { + return []; + } + return response.json; }, // In cases where Zapier needs to show an example record to the user, but we are unable to get a live example diff --git a/dev/examples/zapier/triggers/contact.js b/dev/examples/zapier/triggers/contact.js new file mode 100644 index 00000000000..2ba3bd226f8 --- /dev/null +++ b/dev/examples/zapier/triggers/contact.js @@ -0,0 +1,171 @@ +const subscribeHook = (z, bundle) => { + // `z.console.log()` is similar to `console.log()`. + z.console.log('suscribing hook!'); + + // bundle.targetUrl has the Hook URL this app should call when an action is created. + const data = { + url: bundle.targetUrl, + event: bundle.event, + module: 'contact', + action: bundle.inputData.action + }; + + const url = bundle.authData.url + '/api/index.php/zapierapi/hook'; + + // You can build requests and our client will helpfully inject all the variables + // you need to complete. You can also register middleware to control this. + const options = { + url: url, + method: 'POST', + body: data, + }; + + // You may return a promise or a normal data structure from any perform method. + return z.request(options).then((response) => JSON.parse(response.content)); +}; + +const unsubscribeHook = (z, bundle) => { + // bundle.subscribeData contains the parsed response JSON from the subscribe + // request made initially. + z.console.log('unsuscribing hook!'); + + // You can build requests and our client will helpfully inject all the variables + // you need to complete. You can also register middleware to control this. + const options = { + url: bundle.authData.url + '/api/index.php/zapierapi/hook/' + bundle.subscribeData.id, + method: 'DELETE', + }; + + // You may return a promise or a normal data structure from any perform method. + return z.request(options).then((response) => JSON.parse(response.content)); +}; + +const getContact = (z, bundle) => { + // bundle.cleanedRequest will include the parsed JSON object (if it's not a + // test poll) and also a .querystring property with the URL's query string. + const contact = { + id: bundle.cleanedRequest.id, + name: bundle.cleanedRequest.name, + name_alias: bundle.cleanedRequest.name_alias, + firstname: bundle.cleanedRequest.firstname, + address: bundle.cleanedRequest.address, + zip: bundle.cleanedRequest.zip, + town: bundle.cleanedRequest.town, + email: bundle.cleanedRequest.email, + phone_pro: bundle.cleanedRequest.phone_pro, + phone_perso: bundle.cleanedRequest.phone_perso, + phone_mobile: bundle.cleanedRequest.phone_mobile, + authorId: bundle.cleanedRequest.authorId, + createdAt: bundle.cleanedRequest.createdAt, + action: bundle.cleanedRequest.action + }; + + return [contact]; +}; + +const getFallbackRealContact = (z, bundle) => { + // For the test poll, you should get some real data, to aid the setup process. + const module = bundle.inputData.module; + const options = { + url: bundle.authData.url + '/api/index.php/contacts/0', + }; + + return z.request(options).then((response) => [JSON.parse(response.content)]); +}; + +// const getModulesChoices = (z/*, bundle*/) => { +// // For the test poll, you should get some real data, to aid the setup process. +// const options = { +// url: bundle.authData.url + '/api/index.php/zapierapi/getmoduleschoices', +// }; + +// return z.request(options).then((response) => JSON.parse(response.content)); +// }; +// const getModulesChoices = () => { +// return { +// orders: "Order", +// invoices: "Invoice", +// contacts: "Contact", +// contacts: "Contacts" +// }; +// }; + +// const getActionsChoices = (z, bundle) => { +// // For the test poll, you should get some real data, to aid the setup process. +// const module = bundle.inputData.module; +// const options = { +// url: url: bundle.authData.url + '/api/index.php/zapierapi/getactionschoices/thirparty`, +// }; + +// return z.request(options).then((response) => JSON.parse(response.content)); +// }; + +// We recommend writing your triggers separate like this and rolling them +// into the App definition at the end. +module.exports = { + key: 'contact', + + // You'll want to provide some helpful display labels and descriptions + // for users. Zapier will put them into the UX. + noun: 'Contact', + display: { + label: 'New Contact', + description: 'Triggers when a new contact action is done in Dolibarr.' + }, + + // `operation` is where the business logic goes. + operation: { + + // `inputFields` can define the fields a user could provide, + // we'll pass them in as `bundle.inputData` later. + inputFields: [ + { + key: 'action', + required: true, + type: 'string', + helpText: 'Which action of contact this should trigger on.', + choices: { + create: "Create", + modify: "Modify", + validate: "Validate", + } + } + ], + + type: 'hook', + + performSubscribe: subscribeHook, + performUnsubscribe: unsubscribeHook, + + perform: getContact, + performList: getFallbackRealContact, + + // In cases where Zapier needs to show an example record to the user, but we are unable to get a live example + // from the API, Zapier will fallback to this hard-coded sample. It should reflect the data structure of + // returned records, and have obviously dummy values that we can show to any user. + sample: { + id: 1, + createdAt: 1472069465, + lastname: 'DOE', + firstname: 'John', + authorId: 1, + action: 'create' + }, + + // If the resource can have fields that are custom on a per-user basis, define a function to fetch the custom + // field definitions. The result will be used to augment the sample. + // outputFields: () => { return []; } + // Alternatively, a static field definition should be provided, to specify labels for the fields + outputFields: [ + {key: 'id', type: "integer", label: 'ID'}, + {key: 'createdAt', label: 'Created At'}, + {key: 'lastname', label: 'Lastname'}, + {key: 'firstname', label: 'Firstname'}, + {key: 'phone', label: 'Phone pro'}, + {key: 'phone_perso', label: 'Phone perso'}, + {key: 'phone_mobile', label: 'Phone mobile'}, + {key: 'authorId', type: "integer", label: 'Author ID'}, + {key: 'action', label: 'Action'} + ] + } +}; diff --git a/dev/examples/zapier/triggers/member.js b/dev/examples/zapier/triggers/member.js new file mode 100644 index 00000000000..3385cdca625 --- /dev/null +++ b/dev/examples/zapier/triggers/member.js @@ -0,0 +1,171 @@ +const subscribeHook = (z, bundle) => { + // `z.console.log()` is similar to `console.log()`. + z.console.log('suscribing hook!'); + + // bundle.targetUrl has the Hook URL this app should call when an action is created. + const data = { + url: bundle.targetUrl, + event: bundle.event, + module: 'member', + action: bundle.inputData.action + }; + + const url = bundle.authData.url + '/api/index.php/zapierapi/hook'; + + // You can build requests and our client will helpfully inject all the variables + // you need to complete. You can also register middleware to control this. + const options = { + url: url, + method: 'POST', + body: data, + }; + + // You may return a promise or a normal data structure from any perform method. + return z.request(options).then((response) => JSON.parse(response.content)); +}; + +const unsubscribeHook = (z, bundle) => { + // bundle.subscribeData contains the parsed response JSON from the subscribe + // request made initially. + z.console.log('unsuscribing hook!'); + + // You can build requests and our client will helpfully inject all the variables + // you need to complete. You can also register middleware to control this. + const options = { + url: bundle.authData.url + '/api/index.php/zapierapi/hook/' + bundle.subscribeData.id, + method: 'DELETE', + }; + + // You may return a promise or a normal data structure from any perform method. + return z.request(options).then((response) => JSON.parse(response.content)); +}; + +const getMember = (z, bundle) => { + // bundle.cleanedRequest will include the parsed JSON object (if it's not a + // test poll) and also a .querystring property with the URL's query string. + const member = { + id: bundle.cleanedRequest.id, + name: bundle.cleanedRequest.name, + name_alias: bundle.cleanedRequest.name_alias, + firstname: bundle.cleanedRequest.firstname, + address: bundle.cleanedRequest.address, + zip: bundle.cleanedRequest.zip, + town: bundle.cleanedRequest.town, + email: bundle.cleanedRequest.email, + phone_pro: bundle.cleanedRequest.phone_pro, + phone_perso: bundle.cleanedRequest.phone_perso, + phone_mobile: bundle.cleanedRequest.phone_mobile, + authorId: bundle.cleanedRequest.authorId, + createdAt: bundle.cleanedRequest.createdAt, + action: bundle.cleanedRequest.action + }; + + return [member]; +}; + +const getFallbackRealMember = (z, bundle) => { + // For the test poll, you should get some real data, to aid the setup process. + const module = bundle.inputData.module; + const options = { + url: bundle.authData.url + '/api/index.php/members/0', + }; + + return z.request(options).then((response) => [JSON.parse(response.content)]); +}; + +// const getModulesChoices = (z/*, bundle*/) => { +// // For the test poll, you should get some real data, to aid the setup process. +// const options = { +// url: bundle.authData.url + '/api/index.php/zapierapi/getmoduleschoices', +// }; + +// return z.request(options).then((response) => JSON.parse(response.content)); +// }; +// const getModulesChoices = () => { +// return { +// orders: "Order", +// invoices: "Invoice", +// members: "Member", +// members: "Members" +// }; +// }; + +// const getActionsChoices = (z, bundle) => { +// // For the test poll, you should get some real data, to aid the setup process. +// const module = bundle.inputData.module; +// const options = { +// url: url: bundle.authData.url + '/api/index.php/zapierapi/getactionschoices/thirparty`, +// }; + +// return z.request(options).then((response) => JSON.parse(response.content)); +// }; + +// We recommend writing your triggers separate like this and rolling them +// into the App definition at the end. +module.exports = { + key: 'member', + + // You'll want to provide some helpful display labels and descriptions + // for users. Zapier will put them into the UX. + noun: 'Member', + display: { + label: 'New Member', + description: 'Triggers when a new member action is done in Dolibarr.' + }, + + // `operation` is where the business logic goes. + operation: { + + // `inputFields` can define the fields a user could provide, + // we'll pass them in as `bundle.inputData` later. + inputFields: [ + { + key: 'action', + required: true, + type: 'string', + helpText: 'Which action of member this should trigger on.', + choices: { + create: "Create", + modify: "Modify", + validate: "Validate", + } + } + ], + + type: 'hook', + + performSubscribe: subscribeHook, + performUnsubscribe: unsubscribeHook, + + perform: getMember, + performList: getFallbackRealMember, + + // In cases where Zapier needs to show an example record to the user, but we are unable to get a live example + // from the API, Zapier will fallback to this hard-coded sample. It should reflect the data structure of + // returned records, and have obviously dummy values that we can show to any user. + sample: { + id: 1, + createdAt: 1472069465, + lastname: 'DOE', + firstname: 'John', + authorId: 1, + action: 'create' + }, + + // If the resource can have fields that are custom on a per-user basis, define a function to fetch the custom + // field definitions. The result will be used to augment the sample. + // outputFields: () => { return []; } + // Alternatively, a static field definition should be provided, to specify labels for the fields + outputFields: [ + {key: 'id', type: "integer", label: 'ID'}, + {key: 'createdAt', label: 'Created At'}, + {key: 'lastname', label: 'Lastname'}, + {key: 'firstname', label: 'Firstname'}, + {key: 'phone', label: 'Phone pro'}, + {key: 'phone_perso', label: 'Phone perso'}, + {key: 'phone_mobile', label: 'Phone mobile'}, + {key: 'authorId', type: "integer", label: 'Author ID'}, + {key: 'action', label: 'Action'} + ] + } +}; diff --git a/dev/examples/zapier/triggers/thirdparty.js b/dev/examples/zapier/triggers/thirdparty.js index 0fecd4434ce..76194acbc9a 100644 --- a/dev/examples/zapier/triggers/thirdparty.js +++ b/dev/examples/zapier/triggers/thirdparty.js @@ -118,7 +118,7 @@ module.exports = { noun: 'Thirdparty', display: { label: 'New Thirdparty', - description: 'Triggers when a new thirdpaty action is done in Dolibarr.' + description: 'Triggers when a new thirdparty action is done in Dolibarr.' }, // `operation` is where the business logic goes. diff --git a/doc/install/README b/doc/install/README index 0c7341b196c..0192ff27521 100644 --- a/doc/install/README +++ b/doc/install/README @@ -19,7 +19,7 @@ Download Install -------------------------------- -* For a Quick guide, take a look at README file into root directory. +* For a Quick guide, take a look at README.md file into root directory. * More complete documentations are also available on line on the Dolibarr Wiki: https://wiki.dolibarr.org diff --git a/doc/install/README-DE b/doc/install/README-DE index 047e1915d18..f4cb3c1a6f5 100644 --- a/doc/install/README-DE +++ b/doc/install/README-DE @@ -23,7 +23,7 @@ Download / Herunterladen Installation / Hilfe ------------------------------------ -* Für eine kurze Einleitung schau in die README Datei im Hauptverzeichnis. +* Für eine kurze Einleitung schau in die README.md Datei im Hauptverzeichnis. * Umfangreiche Dokumentationen sind im Dolibarr Wiki zu finden: https://wiki.dolibarr.org/index.php/Hauptseite diff --git a/doc/install/README-FR b/doc/install/README-FR index 5872ec0e3d1..c362316b39c 100644 --- a/doc/install/README-FR +++ b/doc/install/README-FR @@ -20,9 +20,7 @@ https://www.dolistore.com Documentation utilisateur -------------------------------- -* Pour une prise en main et installation rapide, consultez le fichier -README-FR à la racine. +* Pour une prise en main et installation rapide, consultez le fichier README-FR.md à la racine. -* Une documentation utilisateur francophone plus consistante est disponible en -ligne sur le wiki de Dolibarr à l'adresse: -https://wiki.dolibarr.org/index.php/Accueil +* Une documentation utilisateur francophone plus consistante est disponible en ligne sur le wiki de Dolibarr à l'adresse: + https://wiki.dolibarr.org diff --git a/doc/user/README b/doc/user/README index 129dff11058..ecde765cde3 100644 --- a/doc/user/README +++ b/doc/user/README @@ -4,4 +4,5 @@ User guide -------------------------------- * All Dolibarr guides are available, on line, on the Dolibarr Web site: + https://www.dolibarr.org diff --git a/doc/user/README-DE.md b/doc/user/README-DE similarity index 99% rename from doc/user/README-DE.md rename to doc/user/README-DE index 4c7a635a522..336e7ab7085 100644 --- a/doc/user/README-DE.md +++ b/doc/user/README-DE @@ -7,9 +7,7 @@ Benutzeranleitung Alle Dolibarr-Informationen sind online verfuegbar ueber die Webseiten: - https://www.dolibarr.de (de) oder https://www.dolibarr.org (intl) https://wiki.dolibarr.org/index.php/Hauptseite (de) - diff --git a/doc/user/README-FR b/doc/user/README-FR index fbf67fd89bc..f5cb72ea1c6 100644 --- a/doc/user/README-FR +++ b/doc/user/README-FR @@ -3,9 +3,6 @@ README (french) Documentation utilisateur -------------------------------- -* Pour une prise en main et installation rapide, consultez le fichier -README-FR à la racine. +La documentation utilisateur francophone est disponible en ligne sur le site Web de Dolibarr à l'adresse: -* Une documentation utilisateur francophone plus consistante est disponible en -ligne sur le site Web de Dolibarr à l'adresse: https://www.dolibarr.fr diff --git a/htdocs/accountancy/admin/categories_list.php b/htdocs/accountancy/admin/categories_list.php index c5091ef4207..9a8a84893fe 100644 --- a/htdocs/accountancy/admin/categories_list.php +++ b/htdocs/accountancy/admin/categories_list.php @@ -444,7 +444,7 @@ if ($search_country_id > 0) { } else { $sql .= " WHERE "; } - $sql .= " (a.fk_country = ".$search_country_id." OR a.fk_country = 0)"; + $sql .= " (a.fk_country = ".((int) $search_country_id)." OR a.fk_country = 0)"; } // If sort order is "country", we use country_code instead diff --git a/htdocs/accountancy/bookkeeping/balance.php b/htdocs/accountancy/bookkeeping/balance.php index a35333020cd..b84fe255760 100644 --- a/htdocs/accountancy/bookkeeping/balance.php +++ b/htdocs/accountancy/bookkeeping/balance.php @@ -388,7 +388,7 @@ if ($action != 'export_csv') { } elseif (empty($tmparrayforrootaccount['label'])) { // $tmparrayforrootaccount['label'] not defined = the account has not parent with a parent. // This is useless, we should not create a new account when an account has no parent, we must edit it to fix its parent. - // BUG 1: Accounts on level root or level 1 must not have a parent 2 level higher, so shoule not show a link to create another account. + // BUG 1: Accounts on level root or level 1 must not have a parent 2 level higher, so should not show a link to create another account. // BUG 2: Adding a link to create a new accounting account here is useless because it is not add as parent of the orphelin. //$link = '' . img_edit_add() . ''; } @@ -401,14 +401,14 @@ if ($action != 'export_csv') { print '
| '.$langs->trans("Filter").' | |
| '.$langs->trans("Member").' | '; +print img_picto('', 'company', 'class="pictofixedwidth"'); print $form->select_company($id,'memberid','',1); print ' |
| '.$langs->trans("User").' | '; -print $form->select_dolusers($userid, 'userid', 1, '', 0, '', '', 0, 0, 0, '', 0, '', 'maxwidth300'); +print img_picto('', 'user', 'class="pictofixedwidth"'); +print $form->select_dolusers($userid, 'userid', 1, '', 0, '', '', 0, 0, 0, '', 0, '', 'widthcentpercentminusx maxwidth300'); print ' |
| '.$langs->trans("AgendaExtNb", $key)." | "; // Name - print ''; + print ' | '; // URL - print ' | '; + print ' | '; // Offset TZ - print ' | '; + print ' | '; // Color (Possible colors are limited by Google) print ' | '; //print $formadmin->selectColor($conf->global->$color, "google_agenda_color".$key, $colorlist); - print $formother->selectColor((GETPOST("AGENDA_EXT_COLOR".$key) ?GETPOST("AGENDA_EXT_COLOR".$key) : $conf->global->$color), "AGENDA_EXT_COLOR".$key, 'extsitesconfig', 1, '', 'hideifnotset'); + print $formother->selectColor((GETPOST("AGENDA_EXT_COLOR".$key) ?GETPOST("AGENDA_EXT_COLOR".$key) : getDolGlobalString($color)), "AGENDA_EXT_COLOR".$key, 'extsitesconfig', 1, '', 'hideifnotset'); print ' | '; print "
| '.$langs->trans("Name").' | '; @@ -272,7 +273,9 @@ foreach ($dirmodels as $reldir) { } } } -print "
| '.$langs->trans("Name").' | '; @@ -414,6 +418,7 @@ foreach ($dirmodels as $reldir) { } print '
| '.$langs->trans("Parameter").' | '; @@ -469,6 +476,7 @@ print "
| '.$langs->trans("Name").' | '; print ''.$langs->trans("Description").' | '; print ''.$langs->trans("Status")." | \n"; @@ -498,6 +498,7 @@ print load_fiche_titre($langs->trans("SuggestedPaymentModesIfNotDefinedInOrder") print '"; @@ -594,6 +597,8 @@ print '
| '.$langs->trans("Parameter").' | '; @@ -704,6 +709,8 @@ if ($conf->stock->enabled) { */ print '
| '.$langs->trans("Parameter").' | '; @@ -725,6 +734,7 @@ print ''; print " |
| '.$langs->trans("CompanyIds").' | '.$langs->trans("Value").' |
| '.$langs->trans("CompanyIds").' | '.$langs->trans("Value").' |
| '; -print ' | |
| '; print $form->textwithpicto($langs->trans("GDPRContact"), $langs->trans("GDPRContactDesc")); print ' | '; -print 'global->MAIN_INFO_GDPR) ? $conf->global->MAIN_INFO_GDPR : ''))).'"> |
| '; @@ -656,6 +659,7 @@ print ' |
| '.$langs->trans("SocialNetworksInformation").' | '.$langs->trans("Url").' | '.$langs->trans("SocialNetworkId").' | '; @@ -116,17 +118,18 @@ foreach ($listofnetworks as $key => $value) { print ' | '; $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($key).'_URL'; $networkconstid = 'MAIN_INFO_SOCIETE_'.strtoupper($key); - print ' | '; + print ' | '; print ''; print ' | '; print ''; print ' | '; - print ''.dol_print_socialnetworks((!empty($conf->global->$networkconstid) ? dol_escape_htmltag($conf->global->$networkconstid) : ''), 0, 0, $key, $listofnetworks).' | '; + print ''.dol_print_socialnetworks((!empty($conf->global->$networkconstid) ? dol_escape_htmltag($conf->global->$networkconstid) : ''), 0, 0, $key, $listofnetworks).' | '; print '
| '.$langs->trans("Name").' | '; @@ -411,6 +412,7 @@ foreach ($dirmodels as $reldir) { } print '
| '.$langs->trans("Name").' | '; @@ -558,6 +561,7 @@ foreach ($dirmodels as $reldir) { } } print '
| '.$langs->trans("Parameter").' | '; @@ -740,14 +753,14 @@ print ' print '|||
| ';
print $form->textwithpicto($langs->trans("WatermarkOnDraftBill"), $htmltext, 1, 'help', '', 0, 2, 'watermarktooltip').' '; print ' | ';
-print ''; +print ' | '; print ' | '; print ''; print " |
| '.$langs->trans("Name").' | '; @@ -328,7 +329,10 @@ foreach ($dirmodels as $reldir) { } } -print '
| '.$langs->trans("Name").' | '; @@ -468,6 +473,7 @@ foreach ($dirmodels as $reldir) { } print '
| '.$langs->trans("Parameter").' | '; @@ -515,7 +523,7 @@ print "';
print $form->textwithpicto($langs->trans("WatermarkOnDraftInterventionCards"), $htmltext, 1, 'help', '', 0, 2, 'watermarktooltip').' '; print ' | '; -print ''; +print ''; print ' | '; print ''; print " |
';
diff --git a/htdocs/admin/knowledgemanagement.php b/htdocs/admin/knowledgemanagement.php
index 5bc994bb086..b8434773f67 100644
--- a/htdocs/admin/knowledgemanagement.php
+++ b/htdocs/admin/knowledgemanagement.php
@@ -23,32 +23,8 @@
*/
// Load Dolibarr environment
-$res = 0;
-// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined)
-if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
- $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
-}
-// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME
-$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1;
-while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
- $i--; $j--;
-}
-if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
- $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
-}
-if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) {
- $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
-}
-// Try main.inc.php using relative path
-if (!$res && file_exists("../main.inc.php")) {
- $res = @include "../main.inc.php";
-}
-if (!$res && file_exists("../../main.inc.php")) {
- $res = @include "../../main.inc.php";
-}
-if (!$res) {
- die("Include of main fails");
-}
+require '../main.inc.php';
+
global $langs, $user;
// Libraries
diff --git a/htdocs/admin/knowledgerecord_extrafields.php b/htdocs/admin/knowledgerecord_extrafields.php
index 823366e1fbe..d6c94e4ceeb 100644
--- a/htdocs/admin/knowledgerecord_extrafields.php
+++ b/htdocs/admin/knowledgerecord_extrafields.php
@@ -27,32 +27,7 @@
*/
// Load Dolibarr environment
-$res = 0;
-// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined)
-if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
- $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
-}
-// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME
-$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1;
-while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
- $i--; $j--;
-}
-if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
- $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
-}
-if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) {
- $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
-}
-// Try main.inc.php using relative path
-if (!$res && file_exists("../main.inc.php")) {
- $res = @include "../main.inc.php";
-}
-if (!$res && file_exists("../../main.inc.php")) {
- $res = @include "../../main.inc.php";
-}
-if (!$res) {
- die("Include of main fails");
-}
+require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once DOL_DOCUMENT_ROOT.'/knowledgemanagement/lib/knowledgemanagement.lib.php';
diff --git a/htdocs/admin/ldap.php b/htdocs/admin/ldap.php
index 18399894ad0..26f0c28172f 100644
--- a/htdocs/admin/ldap.php
+++ b/htdocs/admin/ldap.php
@@ -255,11 +255,13 @@ print '| '.$langs->trans("LDAPDolibarrMapping").' | '; +print ''.$langs->trans("LDAPDolibarrMapping").' | '; print ''.$langs->trans("LDAPLdapMapping").' | '; print ''.$langs->trans("LDAPNamingAttribute").' | '; print "|