Теория :: Доступ к объектам

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Теория :: Доступ к объектам

Сообщение skiperski » 25.09.2005 (Вс) 1:10

Привет всем.

Чего-то я зациклился в таком вот вопросе: Имеется БД какого-то там учёта. Получилось так что БД обслуживает как бы две фирмы. На самом деле обе принадлежат одному и тому же юр.лицу. Поначалу и расчётный счёт был общим. Профили у фирм совершенно разные, но всё же клиентские базы могут перекрываться. Т.е., например, один и тот же клиент может являться для одной фирмы поставщиком, а для другой клиентом.

Назрела необходимость как-то, может быть не физически, а, и желательно, программно, разделить эти две фирмы. Физически не хочу разделять по причине общего счёта. Бухгалтеры должны видеть общее состояние счёта.

Оператор работающий только с фирмой 1 в принципе не должен видеть клиентов другой фирмы, он вообще может не знать о существовании второй. То же самое по отношению к операторам второй фирмы. Кроме того есть операторы с повышенными полномочиями: могут видеть и тех и других. Проблема возникает когда один из "ограниченных" операторов заводит нового клиента который уже является клиентом другой фирмы. Т.к. он ни сном, ни духом о второй фирме, то получается дублирование данных. Можно ограничить полномочия на добавление нового клиента, но это вызовет другие, уже организационые, проблемы: для занесения нового клиента нужно будет постоянно дёргать полномочного оператора, а он может находиться совсем не близко. Теряется оперативность.

Как правильнее поступить в этой ситуации? Какие будут предложения?

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 25.09.2005 (Вс) 1:24

Создать хранимую процедуру AddCutomer? Которая имеет внутренний доступ ко всему, но на которую наложено только разрешение Execute? Чтобы она при вызове смотрела, нет ли уже такого клиента, и если есть, ставила бы ему просто флаг доступности для обоих фирм?
Последний раз редактировалось GSerg 25.09.2005 (Вс) 1:37, всего редактировалось 1 раз.
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 25.09.2005 (Вс) 1:31

Тут же возникает проблема идентификации одинаковых клиентов. Оператор может ввести название с ошибкой, с другими скобками, сокращённо и т.д. Отловить подобные нюансы может только человек, да и то, если он не практикант :) Можно было бы искать похожие и предлагать на выбор их все же новую, но показывать незя :( или всё же можно, но без подробностей? или где? или куда?

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 25.09.2005 (Вс) 1:37

В общем случае, нигде и никуда :)
Можно их отлавливать по чему-нибудь формализованному, например, по ИНН или по БИК... Если ничего формализованного нет, тогда см. предложение 1 :)
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 25.09.2005 (Вс) 1:40

Хотя... Ввёл чел "Фир. 1", по ИНН нашлось имеющееся, открывает тот же чел следующий раз - а там данные вроде те же, но всё же введены по-другому :) Вот и объясняй ему, почему они изменились :)

Можно сделать честно: "Хотите заюзать существующего?". Ведь полномочные операторы же не втихаря это делают? :)
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 25.09.2005 (Вс) 2:13

С полномочными всё понятно. Они видят всех. Могут и поискать похожего. Вроде бы никаких спец. данных в этих фирмах не требуют. Надо бы уточнить. Эта БД досталась мне в "наследство". Абсолютно не структурирована. Пока даже не знаю с какого конца за неё браться. :(

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 25.09.2005 (Вс) 12:39

А нельзя ли выкинуть базу и сделать ее заново?

Если нельзя, то я бы сделал так.

Есть хранимая процедура CheckCustomer, которая проверяет наличие клиента (лучше всего по ИНН) и на которую есть доступ у всех операторов. И есть процедуры CustomerList и CustomerAdd, внутри которых реализована проверка прав, т.е. привилегированный оператор ее выполняет или нет.
Lasciate ogni speranza, voi ch'entrate.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 25.09.2005 (Вс) 12:54

alibek писал(а):А нельзя ли выкинуть базу и сделать ее заново?

В итоге постепенно буду менять, но решение нужно сейчас. Кроме того, переструктурирование БД не отменяет вопроса: как разделить управление двух фирм с одним расчётным счетом, с общей базой клиентов, товаров и пр.
alibek писал(а):Есть хранимая процедура

Нет хранимой процедуры - Access :(

PS: у тебя дома инет появился или ты сегодня работаешь?

Ещё один ЗЫ: Может у кого есть линки по теории проектирования БД, только не абстрактные: типа организыция бинарных деревьев для хранения и доступа к информации неструктурированного вида, а что-нибудь более приземлённое: особенности реализации складского учёта, инвентаризации, остатка кассы, работы с персоналом и клиентами и т.п. Поделитесь, будь ласка.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 25.09.2005 (Вс) 13:24

Access... Access это плохо, тогда только логикой :)

Нужна таблица "Фирмы". Нужна таблица "Клиенты". Нужна таблица "ФирмыКлиенты", которая будет определять, какой клиент к какой фирме относится.
Это как минимум, без этого разбиение будет сделать сложно.

А по тому, как организуются отдельные элементы системы, я напишу только завтра :)
Lasciate ogni speranza, voi ch'entrate.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 25.09.2005 (Вс) 13:40

alibek писал(а):Access... Access это плохо, тогда только логикой :)

Савсэм всё плёхо :(

alibek писал(а):Нужна таблица "Фирмы". Нужна таблица "Клиенты". Нужна таблица "ФирмыКлиенты", которая будет определять, какой клиент к какой фирме относится.

Ну это-то понятно, и это есть. Вопрос следующий: клиент может выступать как поставщик для одной фирмы и как клиент для другой (кстати, как их вместе назвать-то? а то клиент-клиент как-то странно), так и в комбинации - клиент обеих + поставщик для вторй и т.п. Таблица клиент-тип клиента это дело связывает, но пока без привязки к фирме. Т.е. можно выбрать всех клиентов-клиентов или клиентов-поставщиков, или клиентов-прочие и т.д. Теперь связываем клиента с фирмой. При выборе, например, поставщика у нас есть инфа, что клиент является поставщиком и принадлежитт обеим фирмам. Как отсюда определить для какой фирмы он является поставщиком? Никак. Или для ф1 он поставщик, для ф2 - клиент, полномочный оператор видит пересечение этих множеств. Определить для какой из фирм он поставщик, а для какой клиент невозможно. Нужна доп. инф. Например новые категории, Фирма1-поставщик, Фирма2-поставщик, что есть не красиво и не нормализовано.

alibek писал(а):А по тому, как организуются отдельные элементы системы, я напишу только завтра :)

Ждёмс.

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 25.09.2005 (Вс) 23:30

Признаться, совершенно не понял, зачем делить контрагентов на поставщиков и покупателей. Какая разница, в какой роли он выступил первый раз, когда его завели в базе? Или он есть, и в таком случае может выступать в любой роли, или его нет, а на нет и суда нет. Независимо от того, как его завели, набор полей у него будет одинаков - ИНН, БИК, р/с, к/с требуются в любой ситуации.
Или давайте представим себе ситуацию, когда одна компания будет и поставщиком, и заказчиком. Аутсорсинг называется, если я ничего не путаю :).

Далее, на тему области видимости. Если дух заводит уже существующую запись, по можно сделать вид, что он ее завел, а на самом деле заюзать существующую. В идеале, я бы даже сделал так: отсылал бы деду уведомление о том, что произведено дублирование, также посылал бы все данные, введенные духом, и оригинальные данные - для сравнения. Особенно, если дух ошибся, скажем, в названии, но ИНН и БИК ввел правильно. Дело деда в данном случае - решить, какую форму записи оставить в базе.

Хранить в базе данные о видимости, в общем, элементарно, и Алибек об этом уже написал - стандартное many-to-many relationship resolution, ничего нового тут уже не придумаешь.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 26.09.2005 (Пн) 9:19

Ну вот, добрался :)

Вначале дисклаймер, что все, что будет написано ниже, основано на моем опыте и предпочтениях. Кто-то другой, возможно, спроектировал бы данную задачу совсем по другому.


ИСХОДНЫЕ ДАННЫЕ:

Есть несколько фирм (две или больше), которые обслуживают БД. У фирм общие клиенты, все остальное (документы, проводки, счета) разное.

Для начала, в БД заводим табличку:
Код: Выделить всё
FACTORIES
  FACT_ID (counter)
  CODE (string)
  TITLE (string)


Здесь CODE -- это т.с. логин клиентского приложения, оно нужно, чтобы клиентское приложение могло найти само себя (с какой фирмой оно работает в настоящий момент), т.к. FACT_ID нужен будет в дальнейшей работе.

Затем заводим юзеров. Т.к. фирмы работают независимо, то табличка будет такая:
Код: Выделить всё
USERS
  USER_ID (counter)
  FACT_FACT_ID (fk)
  LOGIN (string)
  NAME (string)
  TITLE (string)
  CROSSACCESS (boolean)
  LOCKED (boolean)
  LOCKDATE (datetime)
  LOCKINFO (memo)


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

Теперь клиенты.
Код: Выделить всё
CLIENTS
  CLNT_ID (counter)
  FACT_FACT_ID (fk)
  SPECIAL (boolean)
  ACCOUNT (long)
  TITLE (string)
  POSTADDRESS
  REGADDRESS
  INN
  ...
  NOTES (memo)
  USER


SPECIAL - специальный флаг. Если он установлен, значит данный клиент является фирмой FACT_FACT_ID (фирма -- ведь тоже клиент, у нее тоже есть расчетные счета, реквизиты и пр.). Т.е. при создании новой записи в FACTORIES автоматически нужно создавать записть в CLIENTS с установленным флагом SPECIAL, у остальных клиентов фирмы этот флаг установлен не будет.

Кстати, лицевые счета для обеих фирм будут общими? Я бы рекомендовал сделать общими, тогда их будет легко искать по номеру лицевого счета. Хотя и это непринципиально, просто искать будет неудобнее.

Теперь табличка расчетных счетов:
Код: Выделить всё
ACCOUNTS
  ACNT_ID (counter)
  CLNT_CLNT_ID (fk)
  R_ACC (string)
  KORR_ACC (string)
  BANK (string)
  BIK (string)
  CITY (string)
  ...


Помимо расчетных счетов нужны балансы клиентов. Предлагаю хранить записи по каждому изменению баланса.
Код: Выделить всё
BALANCES
  BLNC_ID (counter)
  ACNT_ACNT_ID (fk)
  DATE (datetime)
  ORDER (long)
  LAST (boolean)
  BALANCE (currency)


Поле LAST не обязательно, просто с ним будет проще находить последнюю (актуальную) запись. Можно и без него, просто вложенным запросом типа (select MAX(DATE) from BALANCES where ACNT_ACNT_ID = ...), но Access плохо понимает вложенные запросы. Поле ORDER (порядковый номер записи для каждого ACNT_ACNT_ID) также необязательно, но с ним будет проще и не придется боятся, что две записи пройдут слишком быстро и DATE у них будет одинаковый.

Другой способ -- фиксировать балансы клиентов не при каждом изменении, а регулярно, например каждый месяц. В этом случае можно фиксировать не только BALANCE, а остаток на начало периода, обороты, остаток на конец периода и пр. финансовую информацию (налоги, прибыль и пр.). Если делать так, то это значительно упростит подготовку финансовой статистики для бухгалтерии -- достаточно будет работать только с табличкой BALANCES, не поднимая все проводки.


У таблица CLIENTS есть поле FACT_FACT_ID, которое указывает, какая организация создала эту запись.
В Access можно создать запрос, который будет возвращать запрос типа такого:
Код: Выделить всё
SELECT *
FROM CLIENTS
WHERE [SPECIAL] = FALSE
  AND ((FACT_FACT_ID = [CurFactory]) OR ([SuperUser] = TRUE))

Здесь [CurFactory] - текущая фирма, а [SuperUser] - привилегированный юзер.


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

Код: Выделить всё
DOC_TYPES
  DTYP_ID (counter)
  TITLE (string)


Код: Выделить всё
DOCUMENTS
  DOCS_ID (counter)
  FACT_FACT_ID (fk)
  DTYP_DTYP_ID (fk)
  DATE (datetime)
  NUMBER (long)
  CLIENT_FROM (fk на CLIENTS)
  CLIENT_TO (fk на CLIENTS)
  ...
  NOTES (memo)
  USER


Код: Выделить всё
OPERATIONS
  OPER_ID (counter)
  DOCS_DOCS_ID (fk)
  SOURCE
  DESTINATION
  AMOUNT (currency)



Если фирма занимается складской деятельностью, то нужно еще фиксировать состояния складов.
Т.е. нужны следующие таблички:

Код: Выделить всё
GOOD_TYPES
  GTYP_ID (counter)
  TITLE (string)
  PARENT (fk на GOOD_TYPES)
  ...


Код: Выделить всё
GOOD_CATEGORIES
  GCAT_ID (counter)
  TITLE (string)
  HIDDEN (boolean)


Код: Выделить всё
GOODS
  GOOD_ID (counter)
  GTYP_GTYP_ID (fk)
  GCAT_GCAT_ID (fk)
  TITLE (string)
  PRICE1 (currency)
  PRICE2 (currency)
  PRICE3 (currency)
  COUNTRY (string)
  ...


Код: Выделить всё
STORAGES
  STRG_ID (counter)
  FACT_FACT_ID (fk)
  DATE (datetime)
  GOOD_GOOD_ID (fk)
  AMOUNT (double)
  PRICE1 (currency)
  PRICE2 (currency)
  PRICE3 (currency)
  RESERVED (double)


Код: Выделить всё
RESERVATIONS
  RSRV_ID (counter)
  FACT_FACT_ID (fk)
  STRG_STRG_ID (fk)
  GOOD_GOOD_ID (fk)
  CLNT_CLNT_ID (fk)
  AMOUNT (double)
  START_DATE (datetime)
  END_DATE (datetime)
  USER




Ввод клиентов лучше всего делать так, как предложил Ennor. Т.е. оператор вводит нового клиента, клиентская программа ищет во всем списке такого клиента (лучше всего, если лицевой счет будет сквозным, тогда можно искать по нему), если клиент уже есть, то выводит предупреждение, что клиент уже есть в базе, содержимое полей, введенных оператором и существующих в БД и предлагает оператору выбрать, что делать дальше.

Помоему так :)
Lasciate ogni speranza, voi ch'entrate.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 27.09.2005 (Вт) 0:59

Ennor писал(а):Признаться, совершенно не понял, зачем делить контрагентов на поставщиков и покупателей.

Не делить, а привязать к группам. Т.е. один и тот же контрагент может входить и в группу поставщиков, и в групу клиентов. Это для того чтобы при выборе хоть как-нибудь уменьшить область выбора, т.к. контрагентов уже более 4 тысяч. Но эта проблема почти снята. Написал быстрый поиск при вводе (не забываем, что это web-приложение). Теперь группировка отошла на второй план, достаточно ввести код, кусок названия или город, чтобы в списке появились варианты для выбора.

Ennor писал(а):Далее, на тему области видимости.

Решил почти так же. При вводе, опять таки благодаря быстрому поиску, предлагаются варианты. Если среди них нет, то "дух" заводит новую запись, а она валиится в группу "новые". Потом пусть "дед" разгребает.

Ennor писал(а):но ИНН и БИК ввел правильно

Это необязательная информация. Заказать рекламу может и частное лицо. Что у каждого паспорт спрашивать? То же самое по отношению к ЮЛ. Полные данные только у поставщиков.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 27.09.2005 (Вт) 1:04

alibek, спасибо огромное. В очередной раз поражаюсь твоей дотошности (в хорошем смысле) :)

Буду думать и брать пример.

А пока вопрос:
alibek писал(а):записи по каждому изменению баланса

Это имеется в виду общий баланс контрагента? Или по конкретной операции?


Да, ещё не понятно назначение поля CODE. В дальнейшем оно нигде не задействовано.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 27.09.2005 (Вт) 9:29

Я имею ввиду, что будут в наличии записи типа:
Код: Выделить всё
01.01.2006   12   123.12
01.01.2006   13   13.12
01.02.2006   12   234.12
01.02.2006   13   123.12
11.02.2006   13   1230.12
01.03.2006   12   12.12
01.03.2006   13   654.12

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

CODE нужно только затем, чтобы можно было идентифицировать фирму.

Т.е. ты ставишь клиентскую часть, запускаешь, заходишь в настройки и задаешь параметр "Идентификатор фирмы" равным "Фирма1". Затем в другой конторе тоже ставишь клиентскую часть, но параметр "Идентификатор фирмы" делаешь равным "Фирма2".

Когда клиентское приложение запускается, оно обращается к БД, к табличке FACTORIES, делает запрос типа SELECT FACT_ID FROM FACTORIES WHERE [CODE] = 'Фирма1' и таким образом узнает свой FACT_ID (по которому в дальнейшем будут фильтроваться клиенты, операции склады и прочее). По крайней мере я не вижу другого способа сделать подобное в Access :)
Lasciate ogni speranza, voi ch'entrate.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 27.09.2005 (Вт) 11:17

alibek писал(а):Но я предполагал, что планируется что-то типа складского учета, а теперь похоже, что это ненужная детализация :)

Планируется. Вопрос в том насколько детально хранить баланс. У тебя - за сутки. Может эффективнее будет за неделю или месяц, или по каждой операции?

alibek писал(а):CODE нужно только затем, чтобы можно было идентифицировать фирму.
...
По крайней мере я не вижу другого способа сделать подобное в Access :)

:shock: А нельзя сразу прописать FACTORY_ID в настройках?

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 27.09.2005 (Вт) 11:21

skiperski писал(а):А нельзя сразу прописать FACTORY_ID в настройках?

Да можно, конечно. Но откуда ты будешь его знать? Тем более, что FACT_ID будет счетчиком и его значение ты предсказать не сможешь.
А так выбрал CODE, который тебе нравится, и создал в БД запись с нужным CODE.
Не говоря уж о том, что CODE гораздо удобнее для человека, чем непонятное число :) И запомнить его легче, если что.
Последний раз редактировалось alibek 27.09.2005 (Вт) 11:24, всего редактировалось 1 раз.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 27.09.2005 (Вт) 11:23

skiperski писал(а):Планируется. Вопрос в том насколько детально хранить баланс. У тебя - за сутки. Может эффективнее будет за неделю или месяц, или по каждой операции?

Нет, я предлагал либо за месяц (ежемесячно), либо при каждом изменении баланса. У каждого подхода есть свои преимущества и недостатки, лично я бы выбрал второй способ (при каждом изменении баланса, т.е. при проводках).
Lasciate ogni speranza, voi ch'entrate.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 27.09.2005 (Вт) 11:52

alibek писал(а):лично я бы выбрал второй способ (при каждом изменении баланса, т.е. при проводках).

Потом кто-то делает проводку задним числом, и получаем ситуацию: Каскадное обновление записей в таблице

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 27.09.2005 (Вт) 11:55

alibek писал(а):Не говоря уж о том, что CODE гораздо удобнее для человека, чем непонятное число :) И запомнить его легче, если что.

Понятно - ЧПП (Человеко-понятный параметр/переменная/пропертя) :)

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 27.09.2005 (Вт) 13:08

skiperski писал(а):Потом кто-то делает проводку задним числом, и получаем ситуацию: Каскадное обновление записей в таблице

Проводки задним числом -- это неправильно :)
Хотя да, теория это одно, а практика другое.
Lasciate ogni speranza, voi ch'entrate.


Вернуться в Народный треп

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 106

    TopList  
cron