Генерация уникального серийного номера для счета для конкретной компании Php - Laravel - PullRequest
0 голосов
/ 05 марта 2020

Я создаю приложение Laravel для генерации счетов на продажу. Несколько человек из нескольких компаний генерируют счета за один раз. У меня есть таблица для счетов-фактур как invoice_table, где id является первичным ключом, а номер уникальным

id | company_id | series |  number | amount

Теперь, чтобы сгенерировать номер счета-фактуры, я делаю; для конкретной компании возьмите счет и добавьте к этому 1.

//assume $inv->prefix is a string which gives me series for particular company.
$series = Invoice::where('series', $inv->prefix)->max('number');
if(isset($series) && strlen($series) > 0){
    $series += 1; 
} else {
    $series = 1;
}
$inv->number = $series;

Теперь проблема в том, что когда два пользователя пытаются сгенерировать счет-фактуру для одной и той же серии одновременно, это дает мне ошибку дублирования, так как числовой столбец в моей таблице уникальный.

Могу ли я сделать что-то вроде

do{
    $series = Invoice::where('series', $inv->prefix)->max('number');
    if(isset($series) && strlen($series) > 0){
        $series += 1; 
    } else {
        $series = 1;
    }
    $inv->number = $series;
} while($inv->save())

Может кто-нибудь помочь мне здесь. Если я получу исключение для повторяющихся записей, код должен снова принести счет и попытаться снова сохранить запись.

Ответы [ 3 ]

1 голос
/ 05 марта 2020

Я предлагаю не просто увеличивать число для нового InvoiceId, создать шаблон для этого ex-

Создать служебный класс -

class NumberUtility
{
    /**
     * This method generates the random number
     *
     * @param int $length
     *
     * @return int
     */
    public static function getRandomNumber($length = 8): int
    {
        $intMin = (10 ** $length) / 10;
        $intMax = (10 ** $length) - 1;

        try {
            $randomNumber = random_int($intMin, $intMax);
        } catch (\Exception $exception) {
            \Log::error('Failed to generate random number Retrying...');
            \Log::debug(' Error: '.$exception->getMessage());
            $randomNumber = self::getRandomNumber($length);
        }
        return $randomNumber;
    }
}

Теперь создайте метод для получения уникальный номер счета, как показано ниже -

public function getUniqueInvoiceNumber($companyId)
{
    $randomNumber = NumberUtility::getRandomNumber(4);
    $invoiceNumber =  (string)$companyId.$randomNumber;
    $exist = Invoice::where('number', $invoiceNumber)->first();
    if ($exist) {
        $invoiceNumber = $this->getUniqueInvoiceNumber($companyId);
    }
    return $invoiceNumber;
}
0 голосов
/ 05 марта 2020

Вы можете использовать функцию uniqid (): PHP:

$series = uniqid(); //current time in microseconds 
0 голосов
/ 05 марта 2020

Вы можете использовать microtime для обработки ваших счетов. Вероятность дублирования записей очень мала; хотя для дизайна я бы выбрал блокировку таблиц (см. полный ответ)

$inv->number = str_replace(' ', '', microtime());
$inv->save();

Несколько человек из разных компаний генерируют счета за раз.

Рандомизация данных или их приращение - не путь к go в этом случае. Если вы генерируете счета на основе пользовательского шаблона, который использует в качестве переменных данные из базы данных, шансы на создание дубликатов высоки. Вам следует заблокировать таблицы, с которыми вы работаете, для текущей транзакции. Это предотвратит вставку дублированных данных. Я не знаю всей вашей структуры базы данных, хотя я предлагаю демонстрационную версию:

LOCK TABLES working_table WRITE, working_table2 WRITE;

INSERT INTO working_table ( ... ) 
VALUES ( ...,
    (SELECT number
    FROM working_table2
    WHERE series = 'prefix'
    ORDERBY number DESC
    LIMIT 1)+1
);

UNLOCK TABLES;

Затем вы можете вызвать оператор sql следующим образом:

DB::select("
    LOCK TABLES working_table WRITE, working_table2 WRITE;

    INSERT INTO working_table ( column ) 
    VALUES (
        (SELECT number
        FROM working_table2
        WHERE series = ?
        ORDERBY number DESC
        LIMIT 1)+1
    );

    UNLOCK TABLES;
", [ 'param1' ]) // these are the parameters to prepare

Примечание: Если вы используете постоянные подключения к базе данных, вы должны обработать все сгенерированные ошибки из приложения Laravel, потому что необработанные ошибки остановят сценарий, не останавливая дочерний дочерний процесс, который поддерживает подключение к базе данных. При удалении дочернего процесса таблицы блокируются! , поскольку база данных блокирует их для сеанса соединения. Пожалуйста, прочитайте этот пост для получения подробной информации об этом поведении.

...