Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: #31247 #31248

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
621 changes: 621 additions & 0 deletions htdocs/preopportunity/COPYING

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions htdocs/preopportunity/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CHANGELOG PREOPPORTUNITY FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)

## 1.0

Initial version
98 changes: 98 additions & 0 deletions htdocs/preopportunity/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Pre-Opportunity Dolibarr Module

Currently in Dolibarr there is no separate place for sales team to keep the details of records which are suspects but they are not yet a Customer or a Prospect. This is the list where sales team fishes to find opportunity by making cold calls, emails etc. Also Sales team will not like to clutter their list of Customer & Prospects with this list or dump of suspects, hence they prefer to keep the list of suspects separately. To resolve this practical challenege faced by Sales team we have come up with this **Pre-Opportunity** module.

This module for Dolibarr is designed to enhance your Sales Force Automation process by allowing you to track, organize, and convert suspects/pre-opportunities into opportunities more effectively. With this module, you can store the details of various suspects/pre-opportunities gathered from different sources, track their status, assign sales personnel, document conversations, and convert them into an opportunity at appropriate time. It also allows for adding custom fields, ensuring flexibility and adaptability to your business needs.

## Features

- **Pre-Opportunity Management**: Store detailed information about each suspect/pre-opportunity, including the source, the current status (Open/Closed), and custom fields as needed.

- **Assign Salesperson**: Assign to specific sales personnel to ensure proper follow-up and accountability.

- **Events for Conversations**: Track conversations and interactions with record by adding events. This helps in maintaining a complete history of all related communication.

- **Lead Conversion**:
- A "Lead Conversion" button is available to easily convert into an opportunity.
- Upon clicking the **Lead Conversion** button, the following happens automatically:
1. A third party is created based on the record's information.
2. Contacts associated with the third party are created.
3. A new project/lead creation page opens, allowing you to add necessary project details.
4. Once the required fields are filled in and saved, a new project/lead is created.

- **Automatic Linking**: After lead conversion, the newly created third party, contacts, and project are automatically linked to the pre-opportunity. Additionally, the status of the original Pre-Opportunity will be updated to **Closed**.

- **Dynamic Source and Follow-up Status**:
- The **Source** (e.g., marketing campaigns, referrals, etc.) and **Follow-up/Sales Status** (e.g., in progress, won, lost) are dynamic and can be customized via the **Setup** -> **Dictionary** section. This allows businesses to tailor these fields based on their processes.

- Add, remove, or modify sources and follow-up status as your business needs evolve.

## Usage

### Adding a New Pre-Opportunity

1. Navigate to the **Pre-Opportunity** section from the main menu.
2. Click on the **New Pre-Opportunity**.
3. Fill out the details, such as name, source, and any custom fields you've added.
4. Assign the Pre-Opportunity to a salesperson if necessary.
5. Save the Pre-Opportunity.

### Managing Conversations

1. Open an existing Pre-Opportunity.
2. Click on the **Events** section to add details of your conversations or meetings.
3. Save the event for future reference.

### Importing Pre-Opportunity

1. Navigate to the **Tools** -> **New Export**.
2. Select the **Pre-Opportunity** option from the list.
3. Upload a CSV or Excel file containing Pre-Opportunity data.
4. The system will import the Pre-Opportunities into the module, provided the CSV or Excel format is valid.

### Exporting Pre-Opportunity

1. Navigate to the **Tools** -> **New Export**.
2. Select the **Pre-Opportunity** option from the list.
3. The system will generate a CSV or Excel file with all the current Pre-Opportunities, which you can download.

### Sending Mass Emails

1. Go to the **Tools** -> **New emailing**.
2. Add the new Email.
3. Navigate to the **Recipients** From **EMailing card**.
4. Select records from the list that you want to include as participants in the email campaign.
5. The system will send the email to all the selected participants.

### Lead Conversion

1. When a Pre-Opportunity is ready to be converted into a project, open the lead's details page.
2. Click the **Lead Conversion** button.
3. The system will automatically create the related third party and contacts.
4. A new project/lead creation page will open—fill out the necessary details and save.
5. The system will mark the original Pre-Opportunity as **Closed**, and link the new third party, contacts, and project with the pre-opportunity.

### Customizing Sources and Contact Types

1. Go to **Setup** -> **Dictionary**.
2. In the **Dictionary** section, you can add, edit, or delete:
- **Sources**: Define where Pre-Opportunity are coming from (e.g., social media, email campaigns, referrals).
- **Contact Type**: Customize Contact Type according to your workflow.


## Custom Fields

You can add custom fields in the Pre-Opportunity module to capture additional data. To add custom fields:

1. Go to the **Setup** -> **Pre-Opportunity** section.
2. Add new fields as needed, which will appear on the Pre-Opportunity creation form.



## License

GPLv3 or (at your option) any later version. See file COPYING for more information.

## Support

For any issues or feature requests, please contact us at [[email protected]].
106 changes: 106 additions & 0 deletions htdocs/preopportunity/admin/about.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php
/* Copyright (C) 2004-2017 Laurent Destailleur <[email protected]>
* Copyright (C) 2024 Johnson
*
* 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 <https://www.gnu.org/licenses/>.
*/

/**
* \file preopportunity/admin/about.php
* \ingroup preopportunity
* \brief About page of module Preopportunity.
*/

// 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");
}

// Libraries
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
require_once '../lib/preopportunity.lib.php';

// Translations
$langs->loadLangs(array("errors", "admin", "preopportunity@preopportunity"));

// Access control
if (!$user->admin) {
accessforbidden();
}

// Parameters
$action = GETPOST('action', 'aZ09');
$backtopage = GETPOST('backtopage', 'alpha');


/*
* Actions
*/

// None


/*
* View
*/

$form = new Form($db);

$help_url = '';
$page_name = "PreopportunityAbout";

llxHeader('', $langs->trans($page_name), $help_url, '', 0, 0, '', '', '', 'mod-preopportunity page-admin_about');

// Subheader
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1').'">'.$langs->trans("BackToModuleList").'</a>';

print load_fiche_titre($langs->trans($page_name), $linkback, 'title_setup');

// Configuration header
$head = preopportunityAdminPrepareHead();
print dol_get_fiche_head($head, 'about', $langs->trans($page_name), 0, 'preopportunity@preopportunity');

dol_include_once('/preopportunity/core/modules/modPreopportunity.class.php');
$tmpmodule = new modPreopportunity($db);
print $tmpmodule->getDescLong();

// Page end
print dol_get_fiche_end();
llxFooter();
$db->close();
148 changes: 148 additions & 0 deletions htdocs/preopportunity/admin/preopportunity_extrafields.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php
/* Copyright (C) 2001-2002 Rodolphe Quiedeville <[email protected]>
* Copyright (C) 2003 Jean-Louis Bergamo <[email protected]>
* Copyright (C) 2004-2011 Laurent Destailleur <[email protected]>
* Copyright (C) 2012 Regis Houssin <[email protected]>
* Copyright (C) 2014 Florian Henry <[email protected]>
* Copyright (C) 2015 Jean-François Ferry <[email protected]>
*
* 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 <https://www.gnu.org/licenses/>.
*/

/**
* \file admin/preopportunity_extrafields.php
* \ingroup preopportunity
* \brief Page to setup extra fields of preopportunity
*/

// 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_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once '../lib/preopportunity.lib.php';

// Load translation files required by the page
$langs->loadLangs(array('preopportunity@preopportunity', 'admin'));

$extrafields = new ExtraFields($db);
$form = new Form($db);

// List of supported format
$tmptype2label = ExtraFields::$type2label;
$type2label = array('');
foreach ($tmptype2label as $key => $val) {
$type2label[$key] = $langs->transnoentitiesnoconv($val);
}

$action = GETPOST('action', 'aZ09');
$attrname = GETPOST('attrname', 'alpha');
$elementtype = 'preopportunity_preopportunity'; //Must be the $table_element of the class that manage extrafield

if (!$user->admin) {
accessforbidden();
}


/*
* Actions
*/

require DOL_DOCUMENT_ROOT.'/core/actions_extrafields.inc.php';



/*
* View
*/

$textobject = $langs->transnoentitiesnoconv("PreOpportunity");

$help_url = '';
$page_name = "PreopportunitySetup";

llxHeader('', $langs->trans("PreopportunitySetup"), $help_url, '', 0, 0, '', '', '', 'mod-preopportunity page-admin_extrafields');


$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
print load_fiche_titre($langs->trans($page_name), $linkback, 'title_setup');


$head = preopportunityAdminPrepareHead();

// print dol_get_fiche_head($head, 'preopportunity_extrafields', $langs->trans($page_name), -1, 'preopportunity@preopportunity');

require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php';

print dol_get_fiche_end();


// Buttons
if ((float) DOL_VERSION < 17) { // On v17+, the "New Attribute" button is included into tpl.
if ($action != 'create' && $action != 'edit') {
print '<div class="tabsAction">';
print '<a class="butAction reposition" href="'.$_SERVER["PHP_SELF"].'?action=create">'.$langs->trans("NewAttribute").'</a>';
print "</div>";
}
}


/*
* Creation of an optional field
*/
if ($action == 'create') {
print '<br><div id="newattrib"></div>';
print load_fiche_titre($langs->trans('NewAttribute'));

require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_add.tpl.php';
}

/*
* Edition of an optional field
*/
if ($action == 'edit' && !empty($attrname)) {
print "<br>";
print load_fiche_titre($langs->trans("FieldEdition", $attrname));

require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_edit.tpl.php';
}

// End of page
llxFooter();
$db->close();
Loading
Loading