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 3 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 [support@accellier.com].
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 <eldy@users.sourceforge.net>
* 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"])) {

Check failure on line 28 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Negated boolean expression is always true.
$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";

Check failure on line 48 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Path in include() "../../../main.inc.php" is not a file or it does not exist.
}
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"));

Check failure on line 60 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Variable $langs might not be defined.

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

Check failure on line 63 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Variable $user might not be defined.
accessforbidden();
}

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


/*
* Actions
*/

// None


/*
* View
*/

$form = new Form($db);

Check failure on line 83 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Variable $db might not be defined.

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

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

Check failure on line 88 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Variable $langs might not be defined.

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

Check failure on line 91 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Variable $langs might not be defined.

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

Check failure on line 93 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Variable $langs might not be defined.

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

Check failure on line 97 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Variable $langs might not be defined.

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

Check failure on line 100 in htdocs/preopportunity/admin/about.php

View workflow job for this annotation

GitHub Actions / phpstan

Variable $db might not be defined.
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 <rodolphe@quiedeville.org>
* Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
* Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2014 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
*
* 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