пятница, 13 марта 2015 г.

1c специалист задача 1.26

Условие из сборника:
Компания занимается оптовой торговлей. Взаиморасчеты с покупателями ведутся в разрезе счетов. Покупателю выставляется счет, на основании которого далее происходит отгрузка документами «Расходная накладная», причем накладных по одному счету может быть несколько. Считается, что товар по счету в конце концов будет отгружен полностью, а накладные могут быть только на основании счета. При выставлении счета каждый раз оговаривается срок (количество дней), в который покупатель должен оплатить товар, причем срок отсчитывается относительно даты полной отгрузки товара по этому счету. В том случае, если товар по счету отгружен полностью, срок оплаты истек, а покупатель оплатил не до конца, ему насчитываются пени. Для расчета пеней пользователями раз в неделю формируется документ «Пени», в котором автоматически должны рассчитываться пени по формуле:
«Кол-во дней просрочки» * «% пени» * «оставшаяся сумма долга по счету». Просрочка отсчитывается от даты полной отгрузки плюс срок оплаты (из счета). Процент пеней также указывается в каждом счете. Пени на пени не начисляются. Складской учет товаров не ведется. Можно считать, что документы задним числом не вводятся, но существующие документы могут перепроводиться. При поступлении оплаты (документ «Приход денег») в первую очередь покрывается задолженность по пеням, а уже потом оставшаяся сумма засчитывается как оплата товара. В документе «Приход денег» указывается покупатель, общая сумма платежа и перечень оплачиваемых счетов, а распределение по задолженности должно происходить автоматически при проведении документа. Следует считать, что переплат нет, т.е. по каждому счету контрагент заплатит не больше своей задолженности. Авансовых платежей нет. Распределение оплат должно происходить согласно дате выставления счета, начиная с самого раннего недооплаченного счета. Необходимо построить отчет для анализа состояния счетов на выбранную дату и анализ счета за период.




Решение:


За основу взял решение предыдущей задачи. Основное отличие - появились пени.  Поэтому и в регистр "Взаиморасчеты" необходимо добавить второй ресурс "Пени".
В документах потребуются косметические изменения.


3) Документ "Счет"
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Движения.Счета.Записывать = Истина;
Движение = Движения.Счета.ДобавитьПриход();
Движение.Период = Дата;
Движение.Счет = Ссылка;
Движение.Сумма = Сумма;
КонецПроцедуры

4) Документ "Расходная накладная"
Чтобы документ вводился только на основании счета потребуется 2 процедуры

Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
Если ТипЗнч(ДанныеЗаполнения) =Тип("ДокументСсылка.Счет") Тогда
 Счет = ДанныеЗаполнения; 
 Контрагент = ДанныеЗаполнения.Контрагент; 
 Дата = ТекущаяДата();
 Иначе
 Сообщение = Новый("СообщениеПользователю"); 
 Сообщение.Текст = "Документ можно вводить только на основании счета"; 
 Сообщение.Сообщить(); 
 КонецЕсли;
КонецПроцедуры


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Если Не ЗначениеЗаполнено(Объект.Счет) Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры

Обработка проведения:

Движения.Счета.Записывать = Истина;
Движения.Взаиморасчеты.Записывать = Истина;
// Блокировка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Счета");
ЭлементБлокировки.УстановитьЗначение("Счет", Счет);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Согласно новой методике проведения сначала делаем движения, затем проверку не ушли ли в минус
Движение = Движения.Счета.ДобавитьРасход();
Движение.Период = Дата;
Движение.Счет = Счет;
Движение.Сумма = СуммаПоДокументу;
Движение = Движения.Взаиморасчеты.ДобавитьПриход();
Движение.Период = Дата;
Движение.Счет = Счет;
Движение.Сумма = СуммаПоДокументу;
Движение.Контрагент = Контрагент;
//записываем движения чтобы они попали в запрос
Движения.Счета.Записать();
// запрос с проверкой
Запрос = Новый("Запрос");
Запрос.Текст ="ВЫБРАТЬ
| СчетаОстатки.СуммаОстаток КАК СуммаСчета
|ИЗ
| РегистрНакопления.Счета.Остатки(&ТочкаИтогов, Счет = &Счет) КАК СчетаОстатки
|ГДЕ
| СчетаОстатки.СуммаОстаток < 0";
Запрос.УстановитьПараметр("Счет",Счет);
ТочкаИтогов = ?(РежимПроведения= РежимПроведенияДокумента.Оперативный, Неопределено, Новый Граница(МоментВремени(),ВидГраницы.Включая));
Запрос.УстановитьПараметр("ТочкаИтогов",ТочкаИтогов);
Результат = Запрос.Выполнить();
Если  Не Результат.Пустой() Тогда 
Выборка = Результат.Выбрать();
Выборка.Следующий();
Если Выборка.СуммаСчета < СуммаПоДокументу Тогда
Отказ = Истина;
Сообщение = Новый("СообщениеПользователю");
Сообщение.Текст = "Сумма документа превышает сумму счета на "+ (-Выборка.СуммаСчета);
Сообщение.Сообщить();
КонецЕсли;
КонецЕсли;



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


Процедура ОбработкаПроведения(Отказ, Режим)
Движения.Взаиморасчеты.Записывать = Истина;
Движения.Взаиморасчеты.Записать() ;
// Блокировка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Взаиморасчеты");
ЭлементБлокировки.УстановитьЗначение("Контрагент", Контрагент);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
Запрос  = Новый("Запрос");
Запрос.Текст = "ВЫБРАТЬ
              | ПриходДенегСписокСчетов.Счет
              |ПОМЕСТИТЬ СписокСчетов
              |ИЗ
              | Документ.ПриходДенег.СписокСчетов КАК ПриходДенегСписокСчетов
              |ГДЕ
              | ПриходДенегСписокСчетов.Ссылка = &Ссылка
              |;
              |
              |////////////////////////////////////////////////////////////////////////////////
              |ВЫБРАТЬ
              | ВзаиморасчетыОстатки.Счет,
              | ВзаиморасчетыОстатки.СуммаОстаток КАК Долг,
              | ВзаиморасчетыОстатки.ПеняОстаток КАК Пеня
              |ИЗ
              | РегистрНакопления.Взаиморасчеты.Остатки(
              | &ТочкаИтогов,
              | Контрагент = &Контрагент
              | И Счет В
              | (ВЫБРАТЬ
              | СписокСчетов.Счет
              | ИЗ
              | СписокСчетов)) КАК ВзаиморасчетыОстатки
              |
              |УПОРЯДОЧИТЬ ПО
              | ВзаиморасчетыОстатки.Счет.Дата
              |ИТОГИ
              | СУММА(Долг),
              | СУММА(Пеня)
              |ПО
              | ОБЩИЕ";
Запрос.УстановитьПараметр("Контрагент",Контрагент);
ТочкаИтогов = ?(Режим= РежимПроведенияДокумента.Оперативный, Неопределено, Новый Граница(МоментВремени(),ВидГраницы.Исключая));
Запрос.УстановитьПараметр("ТочкаИтогов",ТочкаИтогов);
Запрос.УстановитьПараметр("Ссылка",Ссылка);
ВыборкаИтоги = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам,"Общие");
ВыборкаИтоги.Следующий();
Если ВыборкаИтоги.Долг + ВыборкаИтоги.Пеня < Сумма Тогда
Отказ = Истина;
Сообщение = Новый("СообщениеПользователю");
Сообщение.Текст = "Сумма документа превышает сумму долга "+ (ВыборкаИтоги.Долг + ВыборкаИтоги.Пеня);
Сообщение.Сообщить();
Иначе
ОсталосьСписать = Сумма;
// спишем пени
Выборка = ВыборкаИтоги.Выбрать();
Пока Выборка.Следующий() и ОсталосьСписать >0    Цикл
Если Выборка.Пеня > 0 Тогда 
СуммаКСписанию = Мин(ОсталосьСписать, Выборка.Пеня);
Движение = Движения.Взаиморасчеты.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Счет = Выборка.Счет;
Движение.Пеня = СуммаКСписанию;
ОсталосьСписать = ОсталосьСписать - СуммаКСписанию;
КонецЕсли;

КонецЦикла;
Выборка.Сбросить();
// спишем долги по счетам
Пока Выборка.Следующий() и ОсталосьСписать >0   Цикл
Если Выборка.Долг>0 Тогда 
СуммаКСписанию = Мин(ОсталосьСписать, Выборка.Долг);
Движение = Движения.Взаиморасчеты.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Счет = Выборка.Счет;
Движение.Сумма = СуммаКСписанию;
ОсталосьСписать = ОсталосьСписать - СуммаКСписанию;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры




6) Документ "Пени"


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Движения.Взаиморасчеты.Записывать = Истина;
Движения.Взаиморасчеты.Записать() ;

Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Взаиморасчеты");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Счета");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
              | СчетаОстаткиИОбороты.Счет,
              | СчетаОстаткиИОбороты.СуммаКонечныйОстаток,
              | МАКСИМУМ(СчетаОстаткиИОбороты.Регистратор.Дата) КАК ДатаПолнойОтгрузки
              |ПОМЕСТИТЬ Счета
              |ИЗ
              | РегистрНакопления.Счета.ОстаткиИОбороты(, &ТочкаИтогов, Регистратор, , ) КАК СчетаОстаткиИОбороты
              |ГДЕ
              | СчетаОстаткиИОбороты.СуммаКонечныйОстаток = 0
              |
              |СГРУППИРОВАТЬ ПО
              | СчетаОстаткиИОбороты.Счет,
              | СчетаОстаткиИОбороты.СуммаКонечныйОстаток
              |;
              |
              |////////////////////////////////////////////////////////////////////////////////
              |ВЫБРАТЬ
              | Счета.Счет,
              | ЕСТЬNULL(ВзаиморасчетыОстатки.СуммаОстаток, 0) КАК Долг,
              | Счета.Счет.ПроцентПени КАК ПроцентПени,
              | ВзаиморасчетыОстатки.Контрагент,
              | РАЗНОСТЬДАТ(Счета.ДатаПолнойОтгрузки, &Дата, ДЕНЬ) КАК ДнейПросрочки
              |ИЗ
              | РегистрНакопления.Взаиморасчеты.Остатки(
              | &ТочкаИтогов,
              | Счет В
              | (ВЫБРАТЬ
              | Счета.Счет
              | ИЗ
              | Счета)) КАК ВзаиморасчетыОстатки
              | ЛЕВОЕ СОЕДИНЕНИЕ Счета КАК Счета
              | ПО ВзаиморасчетыОстатки.Счет = Счета.Счет";
Запрос.УстановитьПараметр("Дата", Дата);
ТочкаИтогов = ?(РежимПроведения= РежимПроведенияДокумента.Оперативный, Неопределено, Новый Граница(МоментВремени(),ВидГраницы.Исключая));
Запрос.УстановитьПараметр("ТочкаИтогов",ТочкаИтогов);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Движение = Движения.Взаиморасчеты.ДобавитьПриход();
Движение.Период     = Дата;
Движение.Счет       = Выборка.Счет;
Движение.Контрагент = Выборка.Контрагент;
Движение.Пеня      = Выборка.Долг * Выборка.ДнейПросрочки *(Выборка.ПроцентПени/100);
КонецЦикла;
КонецПроцедуры


7) Отчет "Анализ состояния счетов"

Пришлось помучаться, текст запроса получился довольно громоздким, вангую критику от некоторых комрадов)
Текст запроса:

ВЫБРАТЬ
СчетаОстаткиИОбороты.Счет,
МАКСИМУМ(СчетаОстаткиИОбороты.Регистратор.Дата) КАК РегистраторДата
ПОМЕСТИТЬ ОтгруженныеСчета
ИЗ
РегистрНакопления.Счета.ОстаткиИОбороты(, &Период, Регистратор, , ) КАК СчетаОстаткиИОбороты
ГДЕ
СчетаОстаткиИОбороты.СуммаКонечныйОстаток = 0

СГРУППИРОВАТЬ ПО
СчетаОстаткиИОбороты.Счет
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СчетаОстатки.Счет,
СчетаОстатки.СуммаОстаток
ПОМЕСТИТЬ НеотгруженныеСчета
ИЗ
РегистрНакопления.Счета.Остатки(&Период, ) КАК СчетаОстатки
ГДЕ
СчетаОстатки.СуммаОстаток > 0
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВзаиморасчетыОстатки.Счет,
ОтгруженныеСчета.РегистраторДата КАК ДатаПолнойОтгрузки,
ВзаиморасчетыОстатки.СуммаОстаток КАК ЗадолженностьПоСчету,
ВзаиморасчетыОстатки.ПеняОстаток КАК ЗадолженностьПоПеням
ИЗ
РегистрНакопления.Взаиморасчеты.Остатки(
&Период,
Счет В
(ВЫБРАТЬ
НеотгруженныеСчета.Счет КАК Счет
ИЗ
НеотгруженныеСчета КАК НеотгруженныеСчета)
ИЛИ Счет В
(ВЫБРАТЬ
ОтгруженныеСчета.Счет КАК Счет
ИЗ
ОтгруженныеСчета КАК ОтгруженныеСчета)) КАК ВзаиморасчетыОстатки
ЛЕВОЕ СОЕДИНЕНИЕ ОтгруженныеСчета КАК ОтгруженныеСчета
ПО ВзаиморасчетыОстатки.Счет = ОтгруженныеСчета.Счет
ГДЕ
ВзаиморасчетыОстатки.СуммаОстаток > 0

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
НеотгруженныеСчета.Счет,
"",
НеотгруженныеСчета.СуммаОстаток,
0
ИЗ
НеотгруженныеСчета КАК НеотгруженныеСчета


8) Отчет "Анализ счета за период"
ВЫБРАТЬ
ВзаиморасчетыОстаткиИОбороты.Регистратор КАК Документ,
ВзаиморасчетыОстаткиИОбороты.СуммаПриход КАК Задолженность,
ВзаиморасчетыОстаткиИОбороты.СуммаРасход КАК Оплачено,
ВЫБОР
КОГДА ВзаиморасчетыОстаткиИОбороты.ПеняПриход > 0
ТОГДА ВзаиморасчетыОстаткиИОбороты.ПеняПриход
КОГДА ВзаиморасчетыОстаткиИОбороты.ПеняРасход > 0
ТОГДА -ВзаиморасчетыОстаткиИОбороты.ПеняРасход
КОНЕЦ КАК Пеня
ИЗ
РегистрНакопления.Взаиморасчеты.ОстаткиИОбороты(&ДатаНачала, &ДатаОкончания, Регистратор, ДвиженияИГраницыПериода, Счет = &Счет) КАК ВзаиморасчетыОстаткиИОбороты

УПОРЯДОЧИТЬ ПО
ВзаиморасчетыОстаткиИОбороты.Регистратор.Дата

Скачать можно тут

Комментариев нет:

Отправить комментарий